Load data files
library(dplyr)
# specify the directory where the files are located
# For NOAA machines
# dir_path <- "/Users/enrique.montes/Google Drive/My Drive/GDrive/OCED_AOML/WS_cruises/plankton_imaging/CPICS/TS.Master_selection"
# For personal machine
dir_path <- "~/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/OCED_AOML/WS_cruises/plankton_imaging/CPICS/TS.Master_selection"
# obtain a list of file names in the directory
file_names <- list.files(path = dir_path, pattern = ".txt", full.names = TRUE)
# loop over each file and import the tables (use this for DATES)
for (file in file_names) {
table_name <- gsub(".txt", "", basename(file)) # get the name of the table from the file name
assign(table_name, read.table(file = file, header = FALSE, sep = "\t") %>%
mutate(date = as.POSIXct(substr(V1, start = 24, stop = 36), format="%Y%m%d_%H%M", tz="UTC")))
}
# # loop over each file and import the tables (use this for STRINGS)
# for (file in file_names) {
# table_name <- gsub(".txt", "", basename(file)) # get the name of the table from the file name
# assign(table_name, read.table(file = file, header = FALSE, sep = "\t"))
# }
Match file name using DATE values
library(tidyverse)
library(lubridate)
dir_path2 <- "~/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/OCED_AOML/WS_cruises/plankton_imaging/CPICS/ws_cruise_ctd"
file_name <- list.files(path = dir_path2, pattern = "ctd_meta_v3.csv", full.names = TRUE)
ctd_meta <- read.csv(file_name, fill = TRUE)
# USE WITH ctd_meta_v3.csv
dt_list <- as.POSIXct(paste(ctd_meta$year,
sprintf("%02d", ctd_meta$month),
sprintf("%02d", ctd_meta$day),
ctd_meta$time_gmt),
format = "%Y%m%d %I:%M:%S %p",
tz = "UTC")
################################################################################################################
# This section detects short transit times between stations
# Calculate time differences in seconds between consecutive dt_list objects
time_differences <- as.numeric(difftime(dt_list[-1], dt_list[-length(dt_list)], units = "secs"))
# Convert time differences from seconds to minutes
time_differences_mins <- time_differences / 60
# Create a data frame showing the original times and their differences in minutes
time_diff_df <- data.frame(
start_time = dt_list[-length(dt_list)],
end_time = dt_list[-1],
time_difference_mins = time_differences_mins
)
# find CTD time stamps of consecutive stations within less than 20 min. This will identify CTD casts that are close to each other
short_t_idx <- which(time_diff_df$time_difference_mins < 20)
short_timestamps <- dt_list[short_t_idx]
#################################################################################################################
# Create empty data frame to store results
conc_occ_final <- data.frame(date = character(), count = numeric())
# List of class objects to be processed
class_names <- c("Acantharea", "Centric", "Ceratium", "Chaetoceros", "Chaetognaths",
"Chain2", "Chain3", "Ostracods", "Copepods", "Decapods", "Echinoderms",
"Guinardia", "Jellies", "Larvaceans", "Neocalyptrella", "Noctiluca",
"Polychaets", "Pteropods", "Tricho")
# time buffer before and after CTD time in seconds so that CPICS records are matched to CTD times.
start <- 10 * 60
stop <- 10 * 60
# Iterate over dt_list intervals
for (i in 1:length(dt_list)) {
# Initialize a list to store counts for the current interval
counts_list <- list(date = dt_list[i])
# Iterate over each class object and perform subsetting
for (class_name in class_names) {
class_data <- get(paste0("class.", class_name)) # Dynamically get the class data frame
if (i < length(dt_list)) {
# Subsetting for all intervals except the last one
subset_data <- subset(class_data, date >= dt_list[i]-start & date < dt_list[i+1]-stop)
} else {
# Subsetting for the last interval: capture all data greater than or equal to the last dt_list
subset_data <- subset(class_data, date >= dt_list[i]-start)
}
counts_list[[class_name]] <- nrow(subset_data)
}
# Convert counts_list to a data frame and bind it to the result
result <- as.data.frame(counts_list)
conc_occ_final <- rbind(conc_occ_final, result)
}
# Combine with ctd_meta
taxa_meta <- cbind(ctd_meta, conc_occ_final)
################################################################################
# Check for unaccounted CPICS records
# Initialize a list to store unaccounted dates for each class
unaccounted_dates_list <- list()
# Iterate over each class object
for (class_name in class_names) {
# Get the date-time objects from the current class
sel_class_dates <- get(paste0("class.", class_name))$date
# Initialize a logical vector to track whether each class date is accounted for
is_accounted_for <- rep(FALSE, length(sel_class_dates))
# Check each class date against the intervals in dt_list
for (i in 1:length(dt_list)) {
if (i < length(dt_list)) {
# Check all intervals except the last one
interval_start <- dt_list[i] - start
interval_end <- dt_list[i + 1] - stop
} else {
# Last interval captures all data greater than or equal to the last dt_list
interval_start <- dt_list[i] - start
interval_end <- Inf # Effectively no upper bound
}
# Mark class dates that fall within the current interval as accounted for
is_accounted_for <- is_accounted_for | (sel_class_dates >= interval_start & sel_class_dates < interval_end)
}
# Subset the class dates that were not accounted for
unaccounted_sel_class_dates <- sel_class_dates[!is_accounted_for]
# Store the unaccounted dates in the list
unaccounted_dates_list[[class_name]] <- unaccounted_sel_class_dates
}
# Print or view the unaccounted dates for each class
for (class_name in class_names) {
cat("Unaccounted dates for class:", class_name, "\n")
print(unaccounted_dates_list[[class_name]])
cat("\n")
}
Unaccounted dates for class: Acantharea
POSIXct of length 0
Unaccounted dates for class: Centric
POSIXct of length 0
Unaccounted dates for class: Ceratium
POSIXct of length 0
Unaccounted dates for class: Chaetoceros
POSIXct of length 0
Unaccounted dates for class: Chaetognaths
POSIXct of length 0
Unaccounted dates for class: Chain2
POSIXct of length 0
Unaccounted dates for class: Chain3
POSIXct of length 0
Unaccounted dates for class: Ostracods
POSIXct of length 0
Unaccounted dates for class: Copepods
POSIXct of length 0
Unaccounted dates for class: Decapods
POSIXct of length 0
Unaccounted dates for class: Echinoderms
POSIXct of length 0
Unaccounted dates for class: Guinardia
POSIXct of length 0
Unaccounted dates for class: Jellies
POSIXct of length 0
Unaccounted dates for class: Larvaceans
POSIXct of length 0
Unaccounted dates for class: Neocalyptrella
POSIXct of length 0
Unaccounted dates for class: Noctiluca
POSIXct of length 0
Unaccounted dates for class: Polychaets
POSIXct of length 0
Unaccounted dates for class: Pteropods
POSIXct of length 0
Unaccounted dates for class: Tricho
POSIXct of length 0
# Calculate plankton concentration time series per station
# Calculate species concentration (counts/ml) for each species
# # For zooplankton
# taxa_meta_concentration <- taxa_meta %>%
# mutate(across(c(Acantharea,Chaetognaths,Ostracods,Copepods,Decapods,Echinoderms,Jellies,
# Larvaceans,Polychaets,Pteropods), ~ ./total_vol_sampled * 1e6)) %>%
# select(Station, dec_lat, dec_lon, year, month, date, Avg.chl.a..ug.L.,
# temp..degC., salinity, total_vol_sampled,
# Acantharea,Chaetognaths,Ostracods,Copepods,Decapods,Echinoderms,
# Jellies,Larvaceans,Polychaets,Pteropods) %>%
# filter(!is.na(total_vol_sampled))
#
# # # Transform taxa_meta_concentration to long format
# taxa_meta_long <- taxa_meta_concentration %>%
# pivot_longer(cols = c(Acantharea, Chaetognaths,Ostracods,Copepods,Decapods,
# Echinoderms, Jellies, Larvaceans,Polychaets, Pteropods),
# names_to = "species", values_to = "species_concentration")
# For phytoplankton
taxa_meta_concentration <- taxa_meta %>%
mutate(across(c(Centric,Ceratium,Chaetoceros,Chain2,Chain3,Guinardia,Neocalyptrella,
Noctiluca,Tricho), ~ ./total_vol_sampled * 1e6)) %>%
select(Station, dec_lat, dec_lon, year, month, date, Avg.chl.a..ug.L.,
temp..degC., salinity,total_vol_sampled,
Centric,Ceratium,Chaetoceros,Chain2,Chain3,Guinardia,Neocalyptrella,
Noctiluca,Tricho) %>%
filter(!is.na(total_vol_sampled))
# # Transform taxa_meta_concentration to long format
taxa_meta_long <- taxa_meta_concentration %>%
pivot_longer(cols = c(Centric,Ceratium,Chaetoceros,Chain2,Chain3,Guinardia,Neocalyptrella,
Noctiluca,Tricho),
names_to = "species", values_to = "species_concentration")
# Filter the data for Stations
station_list_fk <- c("WS","21/LK","MR","16","18","10","12","9.5","9","7","2")
# station_list_sr <- c("68","65","64","60","58","57.3","57.2","57.1","57","56","55","54","53","51",
# "49","47","45","41","30","31","33")
# station_list_cal <- c("CAL5","CAL4","CAL3","CAL2","CAL1","RP1","RP2","RP3","RP4","GP5","BG4","BG3",
# "BG2","BG1","BG6", "BG7")
# station_list_vl <- c("V1","V2","V3","V4","V5","V6","V7","V8","V9","L1","L3","L5","L7","L9")
# station_list_tb <- c("AMI9","AMI8","AMI7","AMI6","AMI5","AMI4","AMI3","AMI2","AMI1","TB1","TB2",
# "TB3","TB4","TB5","TB10","CW4","CW3","CW2","CW1")
filtered_taxa_meta_long <- taxa_meta_long %>%
filter(Station %in% station_list_fk)
# Without filtering per group of stations but looking at the entire region
# filtered_taxa_meta_long <- taxa_meta_long
# Calculate mean and standard deviation of species concentration for each date
summary_data <- filtered_taxa_meta_long %>%
group_by(year, month, species) %>%
summarise(mean_concentration = mean(species_concentration),
sd_concentration = sd(species_concentration))
`summarise()` has grouped output by 'year', 'month'. You can override using the `.groups` argument.
# Combine year and month columns into a single date column
summary_data$date <- as.Date(paste(summary_data$year, summary_data$month, "15", sep = "-"))
# custom_pal_hex2 <- c('#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf')
custom_pal_hex2 <- c('darkturquoise','red','#4daf4a','slategray4','orange','dodgerblue','purple', "yellow","pink","violet")
# Acantharea = darkturquoise
# Copepods = red
# echinos = #4daf4a
# Jellies = slategray4
# Larvaceans = orange
# Polychaetes = dodgerblue
# Pteropods = purple
# # Check dominance of groups by calculating the overall mean
# test <- summary_data %>% filter(species == "Polychaetes") %>% summarize(avg = mean(mean_concentration))
# mean(test$avg)
# Plot time series of mean+sd planton concentration per group of sites
zz <- ggplot(summary_data, aes(x = date, y = mean_concentration, fill = species)) +
geom_bar(stat = "identity", alpha = 0.7) +
scale_fill_manual(values = custom_pal_hex2) +
# geom_errorbar(aes(ymin = 0, ymax = mean_concentration + sd_concentration),
# position = position_dodge(width = 0.9), width = 0.2) +
labs(x = "Date", y = "Mean Species Concentration", fill = "Species") +
theme_minimal()
zz

# Filter summary_data for selected only
# Acantharea, Copepods, Echinoderms, Jellies, Larvaceans, Chaetognaths, Polychaetes, Pteropods
# Centric,Ceratium,Chaetoceros,Diatoms,Diatoms2,Guinardia,Neocalyptrella,Noctiluca,Trichodesmium
summary_selected <- filtered_taxa_meta_long %>%
filter(species == "Tricho") %>%
mutate(date = as.Date(paste(year, month, "15", sep = "-")))
# Create the time series boxplot
sel_box <- ggplot(summary_selected, aes(x = as.factor(date), y = species_concentration)) +
geom_boxplot(fill = "white", notch = FALSE, outlier.shape = NA) +
geom_violin(fill = "lightgrey", color = "NA", alpha = 0.7) +
# geom_jitter(aes(color = factor(Station), size = temp..degC.), alpha = 0.9) +
geom_jitter(width = 0.25, aes(color = salinity, size = temp..degC.), alpha = 0.6) +
# geom_jitter(width = 0.25, aes(color = salinity), size = 3, alpha = 0.6) +
labs(x = "Date", y = expression("Density (org. m"^"-3"~")")) +
scale_color_viridis_c(name = "Salinity", option = "mako") +
scale_size_continuous(name = "Temperature (°C)",
limits = c(15, 35),
breaks = c(15, 20, 25, 30, 35),
range = c(1, 4)) +
ylim(-5, 30000) +
theme_minimal() +
# theme_ipsum() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
sel_box

# # Plot concentration time series for all selected sites
# kk <- ggplot(summary_selected, aes(x = date, y = species_concentration, shape = factor(Station))) +
# labs(x = "Date", y = "Species concentration (org.m-3)") +
# geom_point(aes(size = temp..degC.), fill = "darkgrey") + # or colour = factor(Station)
# scale_shape_manual(values = c(0, 1, 2, 3, 4, 5, 6))
# kk
# kk <- ggplot(summary_selected, aes(x = date, y = species_concentration, colour = factor(Station))) +
# labs(x = "Date", y = "Species concentration (org.m-3)") +
# geom_point(aes(size = temp..degC.), alpha = 0.7) + # or colour = factor(Station) +
# # geom_smooth(method = "lm", formula = y ~ poly(x, 4), se = FALSE) + # Add polynomial fit line
# scale_colour_brewer(palette = "Set1")
# kk
# # Find the index of the last row with date == '2023-01-15' and add a dummy row for missing dates
# new_row <- tibble(Station = NA, dec_lat = NA, dec_lon = NA, year = NA,
# month = NA, date = as.Date('2023-03-15'), Avg.chl.a..ug.L. = NA,
# temp..degC. = NA, salinity = NA, species = NA, species_concentration = NA)
# last_index <- max(which(summary_selected$date == as.Date('2023-01-15')))
# summary_selected <- bind_rows(
# summary_selected[1:last_index, ],
# new_row,
# summary_selected[(last_index + 1):nrow(summary_selected), ]
# )
# Calculates total plankton concentrations aggregating data from
selected stations (eg FK)
# Calculate plankton concentration for all species summed up
# For Phyto
taxa_meta_concentration_all <- taxa_meta %>%
mutate(total_concentration = (
Centric + Ceratium + Chaetoceros + Chain2 + Chain3 + Guinardia + Neocalyptrella +
Noctiluca + Tricho) / total_vol_sampled * 1e6) %>%
select(Station, dec_lat, dec_lon, year, month, date, Avg.chl.a..ug.L.,
temp..degC., salinity, total_vol_sampled, total_concentration) %>%
filter(!is.na(total_vol_sampled))
# # For Zooplankton
# taxa_meta_concentration_all <- taxa_meta %>%
# mutate(total_concentration = (
# Acantharea + Chaetognaths + Ostracods + Copepods + Decapods + Echinoderms + Jellies +
# Larvaceans + Polychaets + Pteropods) / total_vol_sampled * 1e6) %>%
# select(Station, dec_lat, dec_lon, year, month, date, Avg.chl.a..ug.L.,
# temp..degC., salinity, total_vol_sampled, total_concentration) %>%
# filter(!is.na(total_vol_sampled))
# Filter the data for Stations
station_list_fk <- c("WS","21/LK","MR","16","18","10","12","9.5","9","7","2")
filtered_taxa_conc_all <- taxa_meta_concentration_all %>%
filter(Station %in% station_list_fk)
# Aggregate total concentration and total volume sampled per year and month
aggregated_concentration <- filtered_taxa_conc_all %>%
group_by(year, month) %>%
summarise(
total_counts = sum(total_concentration * total_vol_sampled / 1e6, na.rm = TRUE), # Sum up all counts
total_vol_sampled = sum(total_vol_sampled, na.rm = TRUE), # Sum up all volume sampled
mean_temp_degC = mean(temp..degC., na.rm = TRUE), # Calculate mean temperature
mean_salinity = mean(salinity, na.rm = TRUE), # Calculate mean salinity
mean_chla = mean(Avg.chl.a..ug.L., na.rm = TRUE)
) %>%
ungroup() %>%
mutate(total_concentration = (total_counts / total_vol_sampled) * 1e6) # Recalculate the concentration
`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.
# Create a Date column for plotting purposes
aggregated_concentration <- aggregated_concentration %>%
mutate(year_month = as.Date(paste(year, month, "01", sep = "-")))
# Create the time series plot
concentration_ts <- ggplot(aggregated_concentration, aes(x = year_month)) +
geom_line(aes(y = total_concentration), color = "blue", size = 1) + # Line plot for total concentration
geom_point(aes(y = total_concentration), color = "red", size = 2) + # Points for total concentration
# geom_line(aes(y = (mean_temp_degC - 20) / (35 - 20) * max(total_concentration)),
# color = "orange", size = 1) + # Line plot for temperature (normalized)
labs(title = "Total Phytoplankton Concentration Time Series",
x = "Date",
y = expression("Total Concentration (org/m"^3~")")) +
scale_x_date(date_breaks = "1 month", date_labels = "%b %Y") + # Show ticks for every month
scale_y_continuous(
name = expression("Total Concentration (org/m"^3~")"), # Primary y-axis label
# sec.axis = sec_axis(~ . * (35 - 20) / max(aggregated_concentration$total_concentration) + 20,
# name = "Temperature (°C)",
# breaks = seq(20, 35, by = 5)) # Secondary y-axis label
) +
theme_minimal() +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
# axis.title.y.right = element_text(color = "orange"), # Color the secondary y-axis label
# axis.line.y.right = element_line(color = "orange"), # Color the secondary y-axis line
# panel.grid.major = element_blank(), # Remove major grid lines
panel.grid.minor = element_blank(), # Remove minor grid lines
# panel.border = element_blank() # Remove panel border if necessary
)
# Print the plot
concentration_ts

# Calculate plankton concentrations per seascape class
spp_df <- taxa_meta[ , c("X8.day.seascapes", "total_vol_sampled", "date",
"Acantharea",
"Copepods",
"Echinoderms",
"Jellies",
"Larvaceans",
"Polychaets",
"Chaetognaths",
"Pteropods")]
# spp_df <- taxa_meta[ , c("X8.day.seascapes", "total_vol_sampled", "date",
# "Ceratium",
# "Chaetoceros",
# "Chain2",
# "Chain3",
# "Guinardia",
# "Neocalyptrella",
# "Tricho")]
# Remove rows with NaN values in X8.day.seascapes and total_vol_sampled
spp_df <- subset(spp_df, !is.na(X8.day.seascapes) & !is.na(total_vol_sampled))
# Calculate total counts per species within each X8.day.seascapes category
spp_count_per_seascape <- spp_df %>%
gather(key = "species", value = "count", -(X8.day.seascapes:date)) %>%
group_by(X8.day.seascapes, species) %>%
summarise(total_count = sum(count)) %>%
ungroup()
`summarise()` has grouped output by 'X8.day.seascapes'. You can override using the `.groups` argument.
# Calculate total volume sampled per X8.day.seascapes category
total_vol_per_seascape <- spp_df %>%
group_by(X8.day.seascapes) %>%
summarise(total_vol_sampled = sum(total_vol_sampled))
# Merge total counts and total volume data frames
spp_concentration <- merge(spp_count_per_seascape, total_vol_per_seascape, by = "X8.day.seascapes")
# Calculate species per cubic meter: might be misleading since it integrates all counts and sampled volumes across cruises to calculate concentrations. It is likely better to use average concentrations as below.
spp_concentration$species_per_cubic_meter <- spp_concentration$total_count / spp_concentration$total_vol_sampled * 1e6
# # Select desired seascapes and reorder categories in X axis
spp_concentration$X8.day.seascapes <- factor(spp_concentration$X8.day.seascapes, levels = c("3", "5", "7", "11", "13", "15","21","27"))
# Filter out seascape class as desired
# exclude_classes <- c(5, 7, 11)
# spp_concentration <- spp_concentration[!spp_concentration$X8.day.seascapes %in% exclude_classes, ]
custom_pal_hex2 <- c('#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf')
# Create the stack plot
concentration_stackplot <- ggplot(spp_concentration, aes(x = X8.day.seascapes, y = species_per_cubic_meter, fill = species)) +
geom_bar(stat = "identity") +
scale_fill_manual(values = custom_pal_hex2) +
labs(x = "Seascape class", y = "Species concentration (org. m"^"-3"~")") +
theme_minimal() +
theme(axis.text.x = element_text(size = 18), # Set X-axis label font size
axis.text.y = element_text(size = 18)) +
theme(axis.title.x = element_text(size = 18),
axis.title.y = element_text(size = 18)) +
theme(legend.text = element_text(size = 16))
concentration_stackplot

# Calculate percentages
spp_concentration_percent <- spp_concentration %>%
group_by(X8.day.seascapes) %>%
mutate(total_concentration = sum(species_per_cubic_meter),
species_percentage = (species_per_cubic_meter / total_concentration) * 100)
# Create the stacked plot
concentration_percent_stackplot <- ggplot(spp_concentration_percent, aes(x = X8.day.seascapes, y = species_percentage, fill = species)) +
geom_bar(stat = "identity") +
scale_fill_manual(values = custom_pal_hex2) +
labs(x = "Seascape class", y = "Relative concentration (%)") +
theme_minimal() +
theme(axis.text.x = element_text(size = 18), # Set X-axis label font size
axis.text.y = element_text(size = 18)) +
theme(axis.title.x = element_text(size = 18),
axis.title.y = element_text(size = 18)) +
theme(legend.text = element_text(size = 16))
concentration_percent_stackplot

# Gather columns containing species counts into key-value pairs and calculate concentrations
spp_concentration_long <- spp_df %>%
gather(key = "species", value = "count", -(X8.day.seascapes:date)) %>%
mutate(concentration = (count / total_vol_sampled) * 1e6)
# Calculate the mean concentration and its standard deviation per species and 8X.day.seascapes category. This is likely more representative since the approach will capture high concentration events (high counts in low volumes) that otherwise get filtered out when using an overall integrated sampled volume and total counts for the total concentration.
avg_concentration_per_class <- spp_concentration_long %>%
group_by(species, `X8.day.seascapes`) %>%
summarize(
mean_concentration = mean(concentration, na.rm = TRUE),
sd_concentration = sd(concentration, na.rm = TRUE)
)
`summarise()` has grouped output by 'species'. You can override using the `.groups` argument.
avg_concentration_per_class$X8.day.seascapes <- factor(avg_concentration_per_class$X8.day.seascapes, levels = c("3", "5", "7", "11", "13", "15","21","27"))
avg_stackplot <- ggplot(avg_concentration_per_class, aes(x = X8.day.seascapes, y = mean_concentration, fill = species)) +
geom_bar(stat = "identity") +
# scale_fill_manual(values = custom_pal_hex2) + # use for zooplankton
scale_colour_brewer(palette = "Set2") + # use for phytoplankton
labs(x = "Seascape class", y = "Mean density (org. m"^"-3"~")") +
theme_minimal() +
theme(axis.text.x = element_text(size = 18), # Set X-axis label font size
axis.text.y = element_text(size = 18)) +
theme(axis.title.x = element_text(size = 18),
axis.title.y = element_text(size = 18)) +
theme(legend.text = element_text(size = 16))
avg_stackplot

# Calculate percentages
avg_concentration_percent <- avg_concentration_per_class %>%
group_by(`X8.day.seascapes`) %>%
mutate(mean_concentration_percent = mean_concentration / sum(mean_concentration) * 100)
avg_percent_stackplot <- ggplot(avg_concentration_percent, aes(x = X8.day.seascapes, y = mean_concentration_percent, fill = species)) +
geom_bar(stat = "identity") +
scale_fill_manual(values = custom_pal_hex2) + # use for zooplankton
# scale_colour_brewer(palette = "Set2") + # use for phytoplankton
labs(x = "Seascape class", y = "Relative concentration (%)") +
theme_minimal() +
theme(axis.text.x = element_text(size = 18), # Set X-axis label font size
axis.text.y = element_text(size = 18)) +
theme(axis.title.x = element_text(size = 18),
axis.title.y = element_text(size = 18)) +
theme(legend.text = element_text(size = 16))
avg_percent_stackplot

Generate plots
library(tidyverse)
library(hrbrthemes)
library(viridis)
# # Load seascape color palette used with Matlab and extract RGB values for observed unique seascapes
# For NOAA machines
# palette_dir <- "/Users/enrique.montes/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/software/matlab/m_map/seascape_cm"
# For personal machine
palette_dir <- "~/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/software/matlab/m_map/seascape_cm"
palette_file <- list.files(path = palette_dir, pattern = "cmap1.csv", full.names = TRUE)
palette_df <- read.csv(palette_file, header = FALSE)
colnames(palette_df) <- c("r", "g", "b")
unique_seascapes <- sort(unique(na.omit(ctd_meta$X8.day.seascapes)))
subset_palette_df <- palette_df[unique_seascapes, ]
# set RGB values for the plots
r_vals <- round(subset_palette_df$r * 255, 0)
g_vals <- round(subset_palette_df$g * 255, 0)
b_vals <- round(subset_palette_df$b * 255, 0)
custom_pal <- cbind(r_vals, g_vals, b_vals)
custom_pal_hex <- rgb(custom_pal[, 1], custom_pal[, 2], custom_pal[, 3], maxColorValue=255)
# pal_final <- c(custom_pal_hex[3], custom_pal_hex[4], custom_pal_hex[5], )
# filter out rows with NA values in column seascapes
df_filtered <- taxa_meta[complete.cases(taxa_meta$X8.day.seascapes),]
# Convert the 'x' column to character
df_filtered$X8.day.seascapes <- as.character(df_filtered$X8.day.seascapes)
# # Reorder seascape categories in X axis
df_filtered$X8.day.seascapes <- factor(df_filtered$X8.day.seascapes, levels = c("3", "5", "7", "11", "13", "15","21","27"))
# Define custom colors for each level
custom_colors <- c("3" = custom_pal_hex[1], "5" = custom_pal_hex[2], "7" = custom_pal_hex[3],
"11" = custom_pal_hex[4], "13" = custom_pal_hex[5], "15" = custom_pal_hex[6],
"21" = custom_pal_hex[7], "27" = custom_pal_hex[8])
# Filter out seascape class as desired
# df_filtered <- df_filtered[df_filtered$X8.day.seascapes != "5", ]
# Plot
# See https://cran.r-project.org/web/packages/viridis/vignettes/intro-to-viridis.html for color palette options
pp <- df_filtered %>%
ggplot( aes(x=X8.day.seascapes, y=Avg.chl.a..ug.L., fill=X8.day.seascapes)) +
geom_boxplot() +
# scale_fill_viridis(option="H", discrete = TRUE, alpha=0.6) +
scale_fill_manual(values = custom_colors) +
geom_jitter(color="grey", size=0.8, alpha=0.4) +
labs(x = "Seascape class") +
labs(y = expression("["*Chl-a*"]"~ mu*"g"~L^-1)) +
# labs(y = expression("Salinity")) +
# labs(y = expression(paste("Temperature (", degree, "C) at 1 m depth"))) +
# labs(y = expression("["*DO*"]"~mg~L^-1)) +
# labs(y = expression("["*NO["x"]*"]" ~ mu*"M")) +
# labs(y = expression("["*PO[4]^"3-"*"]" ~ mu*"M")) +
# labs(y = expression("["*NH[4]^"+"*"]" ~ mu*"M")) +
# theme(axis.title.y = element_text(hjust = 1))
# scale_x_discrete(labels= c("Tropical/Subtropical Upwelling",
# "Tropical Seas",
# "Warm, Blooms, High Nuts",
# "Tropical/Subtropical Transition",
# "Temperate Transition")) +
theme_ipsum() +
theme(
legend.position="none",
plot.title = element_text(size=11)
) +
# ggtitle("A boxplot with jitter") +
# theme(axis.text.x = element_text(angle = 45)) +
ylim(0, 7) +
theme(axis.text.x = element_text(size = 32, family = "Arial"), # Set X-axis label font size
axis.text.y = element_text(size = 32, family = "Arial")) +
theme(axis.title.x = element_text(size = 18, hjust = 0.5, family = "Arial"),
axis.title.y = element_text(size = 18, hjust = 0.5, family = "Arial")) +
theme(legend.text = element_text(size = 32))
pp
Warning: Removed 184 rows containing non-finite values (`stat_boxplot()`).
Warning: Removed 184 rows containing missing values (`geom_point()`).
Create stackplots showing relative frequency using counts only of
plankton taxa per seascape category
# convert abundance columns to relative frequency
# subsets taxa for zooplankton
# df_subset <- df_filtered[ , c("X8.day.seascapes",
# "Acantharea",
# "Copepods",
# "Echinoderms",
# "Jellies",
# "Larvaceans",
# "Polychaetes",
# "Chaetognaths",
# "Pteropods")]
# subsets taxa for phytoplankton
df_subset <- df_filtered[ , c("X8.day.seascapes",
"Ceratium",
"Chaetoceros",
"Chain2",
"Chain3",
"Guinardia",
"Neocalyptrella",
"Tricho")]
# subsets taxa for all species
# df_subset <- df_filtered[ , c("X8.day.seascapes",
# "Ceratium",
# "Chaetoceros",
# "Diatoms",
# "Diatoms2",
# "Guinardia",
# "Neocalyptrella",
# "Trichodesmium",
# "Acantharea",
# "Copepods",
# "Echinoderms",
# "Jellies",
# "Larvaceans",
# "Polychaetes",
# "Chaetognaths",
# "Pteropods")]
# reshape the data to long format
df_long <- tidyr::gather(df_subset, key = "Species", value = "Frequency", -X8.day.seascapes)
# calculate relative frequencies
df_sum <- df_long %>%
group_by(X8.day.seascapes, Species) %>%
summarise(n = sum(Frequency)) %>%
mutate(freq = n / sum(n))
`summarise()` has grouped output by 'X8.day.seascapes'. You can override using the `.groups` argument.
# Seascape class names:
# "Class 11" - Tropical/Subtropical Upwelling
# "Class 15" - Tropical Seas
# "Class 21" - Warm, Blooms, High Nuts
# "Class 3" - Tropical/Subtropical Transition
# "Class 7" - Temperate Transition
# # Select desired seascapes and reorder categories in X axis
df_sum$X8.day.seascapes <- factor(df_sum$X8.day.seascapes, levels = c("3", "5", "7", "11", "13", "15","21","27"))
# Filter out seascape class as desired
exclude_classes <- c(5, 7, 11)
df_sum <- df_sum[!df_sum$X8.day.seascapes %in% exclude_classes, ]
# Use qualitative palettes: https://colorbrewer2.org/#type=qualitative&scheme=Accent&n=7
custom_pal_hex2 <- c('#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33','#a65628','#f781bf')
# custom_pal_hex2 <- c('#1b9e77','#d95f02','#7570b3','#e7298a','#66a61e','#e6ab02','#a6761d')
# create the stackplot
qq <- ggplot(df_sum, aes(x = X8.day.seascapes, y = freq, fill = Species)) +
# scale_fill_viridis(option="Set3", discrete = TRUE, alpha=0.8) +
scale_fill_viridis(discrete = TRUE, alpha=0.8) +
# scale_fill_manual(values = custom_pal_hex2) +
geom_bar(stat = "identity") +
labs(x = "Seascape class") +
labs(y = "Relative frequency") +
# scale_x_discrete(labels= c("Tropical/Subtropical Upwelling",
# "Tropical Seas",
# "Warm, Blooms, High Nuts",
# "Tropical/Subtropical Transition",
# "Temperate Transition")) +
# theme(axis.text.x = element_text(angle = 45)) +
theme_minimal() +
theme(axis.text.x = element_text(size = 18), # Set X-axis label font size
axis.text.y = element_text(size = 18)) +
theme(axis.title.x = element_text(size = 18),
axis.title.y = element_text(size = 18)) +
theme(legend.text = element_text(size = 16))
#theme(axis.text.x = element_text(hjust = 1))
qq

Compute Shannon Index
library(dplyr)
library(vegan)
Loading required package: permute
Loading required package: lattice
This is vegan 2.6-4
# Select species for Shannon calculation
df_subset_shannon <- df_filtered[ , c("X8.day.seascapes",
"Acantharea",
"Copepods",
"Echinoderms",
"Jellies",
"Larvaceans",
"Polychaets",
"Chaetognaths",
"Pteropods",
"Ceratium",
"Chaetoceros",
"Chain2",
"Chain3",
"Guinardia",
"Neocalyptrella",
"Tricho")]
# reshape the data to long format
df_long <- tidyr::gather(df_subset_shannon, key = "Species", value = "Abundance", -X8.day.seascapes)
# Exclude seascapes
exclude_seascapes <- c(5, 7, 11)
# Compute Shannon diversity per seascape class
shannon_df <- df_long %>%
group_by(X8.day.seascapes) %>%
summarise(shannon = diversity(Abundance, index = "shannon"))
# Filter the data to exclude specified seascapes
shannon_df_filtered <- shannon_df[!shannon_df$X8.day.seascapes %in% exclude_seascapes, ]
# create the bar plot of Shannon diversity
ff <- ggplot(shannon_df_filtered, aes(x = X8.day.seascapes, y = shannon, fill = X8.day.seascapes)) +
geom_bar(stat = "identity") +
# scale_fill_viridis(option="plasma", discrete = TRUE, alpha=0.6) +
scale_fill_manual(values = custom_colors) +
xlab("Seascape Class") +
ylab("Shannon Diversity") +
# scale_x_discrete(labels= c("Tropical/Subtropical Upwelling",
# "Tropical Seas",
# "Warm, Blooms, High Nuts",
# "Tropical/Subtropical Transition",
# "Temperate Transition")) +
# theme(axis.text.x = element_text(angle = 45)) +
theme(axis.text.x = element_text(size = 32), # Set X-axis label font size
axis.text.y = element_text(size = 32)) +
theme(axis.title.x = element_text(size = 32),
axis.title.y = element_text(size = 32)) +
theme(legend.text = element_text(size = 32)) +
guides()
ff

# # Subset the data to exclude specified seascapes
filtered_taxa_meta <- taxa_meta[complete.cases(taxa_meta$X8.day.seascapes), ]
filtered_taxa_meta <- filtered_taxa_meta[!filtered_taxa_meta$X8.day.seascapes %in% exclude_seascapes, ]
# # Plot sampled seascape frequency
tt <- ggplot(filtered_taxa_meta, aes(x = factor(X8.day.seascapes), fill = factor(X8.day.seascapes))) +
geom_bar(color = "black", alpha = 0.7) +
scale_fill_manual(values = custom_colors) +
labs(x = "Seascape Class", y = "Frequency") +
theme_minimal() +
theme(axis.text.x = element_text(size = 22), # Set X-axis label font size
axis.text.y = element_text(size = 22)) +
theme(axis.title.x = element_text(size = 22),
axis.title.y = element_text(size = 22)) +
theme(legend.text = element_text(size = 22)) +
guides(fill = FALSE)
Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as of ggplot2 3.3.4.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
tt

# # Counts of species per seascape
df_sum_abund <- df_long %>%
group_by(X8.day.seascapes, Species) %>%
summarise(n = sum(Abundance))
`summarise()` has grouped output by 'X8.day.seascapes'. You can override using the `.groups` argument.
df_sum_abund_filt <- df_sum_abund[df_sum_abund$Species == "Pteropods",]
# Filter the data to exclude specified seascapes
df_sum_abund_filt2 <- df_sum_abund_filt[!df_sum_abund_filt$X8.day.seascapes %in% exclude_seascapes, ]
dd <- ggplot(df_sum_abund_filt2, aes(x = factor(X8.day.seascapes), y = n, fill = factor(X8.day.seascapes))) +
geom_bar(color = "black", alpha = 0.7, stat = "identity") +
# scale_fill_viridis(option = "magma", discrete = TRUE, alpha = 0.8)
scale_fill_manual(values = custom_colors) +
theme_minimal()
dd

# Compute counts of selected species per seascape class normalized by frequency of seascapes
# reshape the data to long format
df_long2 <- tidyr::gather(df_subset_shannon, key = "Species", value = "Abundance", -X8.day.seascapes)
spp_seascape_normalized <- df_long2 %>%
filter(Species == "Pteropods") %>%
group_by(X8.day.seascapes) %>%
summarise(spp_count = sum(Abundance))
# Compute frequencies of each seascape class
seascape_frequencies <- df_long2 %>%
count(X8.day.seascapes) %>%
rename(freq = n)
# Merge counts with frequencies
spp_seascape_normalized <- merge(spp_seascape_normalized, seascape_frequencies, by = "X8.day.seascapes")
# Normalize counts by frequency
spp_seascape_normalized$spp_normalized <- (spp_seascape_normalized$spp_count / spp_seascape_normalized$freq) * 1000
spp_seascape_normalized_filt <- spp_seascape_normalized[!spp_seascape_normalized$X8.day.seascapes %in% exclude_seascapes, ]
bb <- ggplot(spp_seascape_normalized_filt, aes(x = factor(X8.day.seascapes), y = spp_normalized, fill = factor(X8.day.seascapes))) +
geom_bar(color = "black", alpha = 0.7, stat = "identity") +
scale_fill_manual(values = custom_colors) +
labs(x = "Seascape Class", y = "Counts / Seascape freq * 1000") +
theme_minimal() +
# theme(axis.text.x = element_text(size = 32), # Set X-axis label font size
# axis.text.y = element_text(size = 32)) +
# theme(axis.title.x = element_text(size = 32),
# axis.title.y = element_text(size = 32)) +
# theme(legend.text = element_text(size = 32)) +
guides()
bb

PC plot
library(ggalt)
# Perform principal component analysis on the count data
# for hydrography
sel_vars <- c(
"X8.day.seascapes",
"salinity",
"Avg.chl.a..ug.L.",
"PO4...uM.",
"NO3.NO2..uM.",
"NH4...uM.",
"temp..degC.")
# for taxonomy
# sel_vars <- c("X8.day.seascapes",
# "Acantharea",
# "Copepods",
# "Echinoderms",
# "Jellies",
# "Larvaceans",
# "Polychaets",
# "Chaetognaths",
# "Pteropods")
# sel_vars <- c("X8.day.seascapes",
# "Ceratium",
# "Chaetoceros",
# "Chain2",
# "Chain3",
# "Guinardia",
# "Neocalyptrella",
# "Tricho")
# sel_vars <- c("X8.day.seascapes",
# "Acantharea",
# "Copepods",
# "Echinoderms",
# "Jellies",
# "Larvaceans",
# "Polychaets",
# "Chaetognaths",
# "Pteropods",
# "Ceratium",
# "Chaetoceros",
# "Chain2",
# "Chain3",
# "Guinardia",
# "Neocalyptrella",
# "Tricho")
# filter out rows with NA values in column seascapes
# df_filtered_pca <- taxa_meta[complete.cases(taxa_meta$X8.day.seascapes), ]
df_filtered_pca <- taxa_meta[complete.cases(taxa_meta[, sel_vars]), ]
# exclude_seascapes <- c(5, 7, 11)
exclude_seascapes <- 0
filt_df_pca <- df_filtered_pca[!df_filtered_pca$X8.day.seascapes %in%
exclude_seascapes, sel_vars]
# Select numeric columns in filt_df_pca
numeric_cols <- sapply(filt_df_pca, is.numeric)
# Identify numeric columns except the first one
numeric_cols <- 2:ncol(filt_df_pca)
# Transform numeric columns to log scale
filt_df_pca[numeric_cols] <- lapply(filt_df_pca[numeric_cols], function(x) log(x + 1))
# pca <- prcomp(filt_df_pca[, c("salinity", "Avg.chl.a..ug.L.", "PO4...uM.", "NO3.NO2..uM.")], scale. = TRUE)
pca <- prcomp(filt_df_pca[, -1], scale. = TRUE)
# Extract PC1 and PC2 scores for each sampling event
# pc_scores <- data.frame(seascape = df_subset$X8.day.seascapes, # for taxonomic analysis
pc_scores <- data.frame(seascape = as.character(filt_df_pca$X8.day.seascapes), # for hydrography
PC1 = pca$x[, 1],
PC2 = pca$x[, 2])
# Create the plot
pc_scores$seascape <- factor(pc_scores$seascape, levels = c("3", "5", "7", "11", "13", "15", "21", "27"))
bb <- ggplot(pc_scores, aes(x = PC1, y = PC2, color = seascape)) +
geom_point() +
labs(x = "PC1", y = "PC2", color = "Seascape")
# add circle around cluster of data points
# custom_colors_pca <- custom_pal_hex[c(1, 5, 6, 7, 8)]
custom_colors_pca <- custom_colors
yy <- ggplot(pc_scores, aes(x = PC1, y = PC2, color = seascape)) +
geom_point() +
stat_ellipse(aes(fill = seascape), level = 0.90, geom = "polygon", alpha = 0.3, color = "black") +
scale_color_manual(values = custom_colors_pca) +
scale_fill_manual(values = custom_colors_pca) +
theme_classic() +
xlim(-2,2) +
ylim(-2,2) +
# xlim(-1,1) +
# ylim(-1,1) +
geom_point(size=2) +
guides(colour = guide_legend(override.aes = list(size=2))) +
theme(axis.text.x = element_text(size = 32), # Set X-axis label font size
axis.text.y = element_text(size = 32)) +
theme(axis.title.x = element_text(size = 32),
axis.title.y = element_text(size = 32)) +
theme(legend.text = element_text(size = 32))
yy
Warning: Removed 102 rows containing non-finite values (`stat_ellipse()`).
Too few points to calculate an ellipse
Too few points to calculate an ellipse
Warning: Removed 102 rows containing missing values (`geom_point()`).
Warning: Removed 102 rows containing missing values (`geom_point()`).

################################################################################################
# # Create PCA with eigenvectors
# Extract principal component scores
pc_scores2 <- pca$x
# Extract eigenvectors
eigenvectors <- pca$rotation
# Calculate the percentage variance explained by each principal component
total_variance <- sum(pca$sdev^2)
pc_var_percent <- round(100 * (pca$sdev^2) / total_variance, 1)
# Convert X8.day.seascapes to a factor
filt_df_pca$X8.day.seascapes <- as.factor(filt_df_pca$X8.day.seascapes)
# # For hydrography
qq <- ggplot(filt_df_pca, aes(x = pc_scores2[,1], y = pc_scores2[,2], color = X8.day.seascapes)) +
geom_point(size = 3, alpha = 0.7) +
scale_color_manual(values = custom_colors_pca) +
geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 1], yend = eigenvectors[2, 1]),
arrow = arrow(length = unit(0.1, "inches")), color = "black") + # Add vector for PC1
geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 2], yend = eigenvectors[2, 2]),
arrow = arrow(length = unit(0.1, "inches")), color = "black") + # Add vector for PC2
geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 3], yend = eigenvectors[2, 3]),
arrow = arrow(length = unit(0.1, "inches")), color = "black") + # Add vector for PC3
geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 4], yend = eigenvectors[2, 4]),
arrow = arrow(length = unit(0.1, "inches")), color = "black") + # Add vector for PC4
geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 5], yend = eigenvectors[2, 5]),
arrow = arrow(length = unit(0.1, "inches")), color = "black") + # Add vector for PC5
geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 6], yend = eigenvectors[2, 6]),
arrow = arrow(length = unit(0.1, "inches")), color = "black") + # Add vector for PC6
geom_text(aes(x = eigenvectors[1, 1], y = eigenvectors[2, 1],
label = paste("PC1 (", pc_var_percent[1], "%: Salinity)", sep = "")),
vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC1
geom_text(aes(x = eigenvectors[1, 2], y = eigenvectors[2, 2],
label = paste("PC2 (", pc_var_percent[2], "%: Chla)", sep = "")),
vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC2
geom_text(aes(x = eigenvectors[1, 3], y = eigenvectors[2, 3],
label = paste("PC3 (", pc_var_percent[3], "%: Phosphate)", sep = "")),
vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC3
geom_text(aes(x = eigenvectors[1, 4], y = eigenvectors[2, 4],
label = paste("PC4 (", pc_var_percent[4], "%: NOx)", sep = "")),
vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC4
geom_text(aes(x = eigenvectors[1, 5], y = eigenvectors[2, 5],
label = paste("PC5 (", pc_var_percent[5], "%: Ammonium)", sep = "")),
vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC5
geom_text(aes(x = eigenvectors[1, 6], y = eigenvectors[2, 6],
label = paste("PC6 (", pc_var_percent[6], "%: Temperature)", sep = "")),
vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC6
labs(x = "PC1", y = "PC2", color = "Seascape class") +
xlim(-1.5, 1.5) +
ylim(-1.5, 1.5) +
guides(colour = guide_legend(override.aes = list(size=5))) +
theme_classic() +
theme(axis.text.x = element_text(size = 18), # Set X-axis label font size
axis.text.y = element_text(size = 18)) +
theme(axis.title.x = element_text(size = 18),
axis.title.y = element_text(size = 18)) +
theme(legend.text = element_text(size = 18)) +
theme(legend.title = element_text(size = 18))
qq
Warning: Removed 196 rows containing missing values (`geom_point()`).

# # # For phytoplankton
# qq2 <- ggplot(filt_df_pca, aes(x = pc_scores2[,1], y = pc_scores2[,3], color = X8.day.seascapes)) +
# geom_point(size = 4) +
# scale_color_manual(values = custom_colors_pca) +
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 1], yend = eigenvectors[2, 1]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC1
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 2], yend = eigenvectors[2, 2]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC2
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 3], yend = eigenvectors[2, 3]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC3
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 4], yend = eigenvectors[2, 4]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC4
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 5], yend = eigenvectors[2, 5]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC5
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 6], yend = eigenvectors[2, 6]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC6
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 7], yend = eigenvectors[2, 7]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC7
# geom_text(aes(x = eigenvectors[1, 1], y = eigenvectors[2, 1],
# label = paste("PC1 (", pc_var_percent[1], "%: Ceratium)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC1
# geom_text(aes(x = eigenvectors[1, 2], y = eigenvectors[2, 2],
# label = paste("PC2 (", pc_var_percent[2], "%: Chaetoceros)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC2
# geom_text(aes(x = eigenvectors[1, 3], y = eigenvectors[2, 3],
# label = paste("PC3 (", pc_var_percent[3], "%: Diatoms)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC3
# geom_text(aes(x = eigenvectors[1, 4], y = eigenvectors[2, 4],
# label = paste("PC4 (", pc_var_percent[4], "%: Diatoms2)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC4
# geom_text(aes(x = eigenvectors[1, 5], y = eigenvectors[2, 5],
# label = paste("PC5 (", pc_var_percent[5], "%: Guinardia)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC5
# geom_text(aes(x = eigenvectors[1, 6], y = eigenvectors[2, 6],
# label = paste("PC6 (", pc_var_percent[6], "%: Neocalyptrella)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC6
# geom_text(aes(x = eigenvectors[1, 7], y = eigenvectors[2, 7],
# label = paste("PC7 (", pc_var_percent[7], "%: Trichodesmium)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC7
# labs(x = "PC1", y = "PC3", color = "X8.day.seascapes") +
# xlim(-1, 1) +
# ylim(-1, 1) +
# guides(colour = guide_legend(override.aes = list(size=2))) +
# theme_classic() +
# theme(axis.text.x = element_text(size = 32), # Set X-axis label font size
# axis.text.y = element_text(size = 32)) +
# theme(axis.title.x = element_text(size = 32),
# axis.title.y = element_text(size = 32)) +
# theme(legend.text = element_text(size = 32))
# qq2
#
#
# # # For zooplankton
# qq3 <- ggplot(filt_df_pca, aes(x = pc_scores2[,1], y = pc_scores2[,2], color = X8.day.seascapes)) +
# geom_point(size = 4) +
# scale_color_manual(values = custom_colors_pca) +
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 1], yend = eigenvectors[2, 1]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC1
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 2], yend = eigenvectors[2, 2]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC2
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 3], yend = eigenvectors[2, 3]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC3
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 4], yend = eigenvectors[2, 4]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC4
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 5], yend = eigenvectors[2, 5]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC5
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 6], yend = eigenvectors[2, 6]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC6
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 7], yend = eigenvectors[2, 7]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC7
# geom_segment(aes(x = 0, y = 0, xend = eigenvectors[1, 8], yend = eigenvectors[2, 8]),
# arrow = arrow(length = unit(0.2, "inches")), color = "black") + # Add vector for PC8
# geom_text(aes(x = eigenvectors[1, 1], y = eigenvectors[2, 1],
# label = paste("PC1 (", pc_var_percent[1], "%: Acantharea)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC1
# geom_text(aes(x = eigenvectors[1, 2], y = eigenvectors[2, 2],
# label = paste("PC2 (", pc_var_percent[2], "%: Copepods)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC2
# geom_text(aes(x = eigenvectors[1, 3], y = eigenvectors[2, 3],
# label = paste("PC3 (", pc_var_percent[3], "%: Echinoderms)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC3
# geom_text(aes(x = eigenvectors[1, 4], y = eigenvectors[2, 4],
# label = paste("PC4 (", pc_var_percent[4], "%: Jellies)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC4
# geom_text(aes(x = eigenvectors[1, 5], y = eigenvectors[2, 5],
# label = paste("PC5 (", pc_var_percent[5], "%: Larvaceans)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC5
# geom_text(aes(x = eigenvectors[1, 6], y = eigenvectors[2, 6],
# label = paste("PC6 (", pc_var_percent[6], "%: Polychaetes)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC6
# geom_text(aes(x = eigenvectors[1, 7], y = eigenvectors[2, 7],
# label = paste("PC7 (", pc_var_percent[7], "%: Chaetognaths)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC7
# geom_text(aes(x = eigenvectors[1, 8], y = eigenvectors[2, 8],
# label = paste("PC8 (", pc_var_percent[8], "%: Pteropods)", sep = "")),
# vjust = -0.5, hjust = 0.5, color = "black") + # Add label for PC8
# labs(x = "PC1", y = "PC2", color = "X8.day.seascapes") +
# xlim(-1, 1) +
# ylim(-1, 1) +
# guides(colour = guide_legend(override.aes = list(size=2))) +
# theme_classic() +
# theme(axis.text.x = element_text(size = 32), # Set X-axis label font size
# axis.text.y = element_text(size = 32)) +
# theme(axis.title.x = element_text(size = 32),
# axis.title.y = element_text(size = 32)) +
# theme(legend.text = element_text(size = 32))
# qq3
Map overall WEIGHTED mean concentration values of selected taxa by
transect
library(ggOceanMaps)
library(leaflet)
library(patchwork)
# For zooplankton
taxa_meta_concentration <- taxa_meta %>%
mutate(across(c(Acantharea,Chaetognaths,Ostracods,Copepods,Decapods,Echinoderms,Jellies,
Larvaceans,Polychaets,Pteropods), ~ ./total_vol_sampled * 1e6)) %>%
select(Station, dec_lat, dec_lon, year, month, date, Avg.chl.a..ug.L.,
temp..degC., salinity, total_vol_sampled,
Acantharea,Chaetognaths,Ostracods,Copepods,Decapods,Echinoderms,
Jellies,Larvaceans,Polychaets,Pteropods) %>%
filter(!is.na(total_vol_sampled))
# # For phytoplankton
# taxa_meta_concentration <- taxa_meta %>%
# mutate(across(c(Centric,Ceratium,Chaetoceros,Chain2,Chain3,Guinardia,Neocalyptrella,
# Noctiluca,Tricho), ~ ./total_vol_sampled * 1e6)) %>%
# select(Station, dec_lat, dec_lon, year, month, date, Avg.chl.a..ug.L.,
# temp..degC., salinity,total_vol_sampled,
# Centric,Ceratium,Chaetoceros,Chain2,Chain3,Guinardia,Neocalyptrella,
# Noctiluca,Tricho) %>%
# filter(!is.na(total_vol_sampled))
# List of class names to loop through
class_names <- c("Acantharea", "Chaetognaths", "Ostracods", "Copepods", "Decapods", "Echinoderms", "Jellies", "Larvaceans", "Polychaets", "Pteropods")
class_names <- "Acantharea"
path_sfer_list <- "~/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/proposals/2022_02_MultiStressor_NOAA/module_1"
sfer_curated <- list.files(path_sfer_list, pattern = "sfer_stations_curated.csv", full.names = TRUE)
sfer_sta_list <- read.csv(sfer_curated, header = TRUE)
# Loop over each class name to generate maps
# for (class_name in class_names) {
# # Filter concentration data for the current class and merges curated station list with the concentration df
concentration_df <- taxa_meta_concentration %>%
left_join(sfer_sta_list %>% filter(station_class %in% "C"), by = c('Station' = 'station_id'))
# Compute weighted average lat/lons and mean variable values for the current class
taxa_concentration_avg <- concentration_df %>%
group_by(line_id) %>%
mutate(weight = get(class_name) / sum(get(class_name), na.rm = TRUE)) %>%
summarise(longitude = weighted.mean(dec_lon, w = weight, na.rm = TRUE),
latitude = weighted.mean(dec_lat, w = weight, na.rm = TRUE),
sel_taxa_mean = mean(get(class_name), na.rm = TRUE),
sel_taxa_sd = sd(get(class_name), na.rm = TRUE))
# Prepare data for mapping
dt <- data.frame(
lon = taxa_concentration_avg$longitude,
lat = taxa_concentration_avg$latitude,
mean_param = taxa_concentration_avg$sel_taxa_mean,
sd_param = taxa_concentration_avg$sel_taxa_sd)
# Add station markers
station_markers <- sfer_sta_list %>% filter(station_class %in% "C")
# Create the map
concentration_map <- basemap(limits = c(-86, -79.5, 24, 28.5), bathymetry = TRUE) +
geom_point(data = dt, aes(x = lon, y = lat, size = mean_param, color = sd_param)) +
scale_color_gradient(low = "yellow", high = "red", na.value = NA, name = "Standard Deviation") +
geom_point(data = station_markers, aes(x = mean_lon, y = mean_lat), color = "black", shape = 3, size = 1.5) +
scale_size_continuous(name = "Average concentration",
breaks = c(250, 500, 1000),
range = c(0, 10),
guide = guide_legend(override.aes = list(color = "black", fill = "white"))) +
theme_minimal() +
ggtitle(paste(class_name))
# Optionally, save each map as an image
ggsave(paste0("concentration_map_", class_name, ".png"), plot = concentration_map, width = 8, height = 6)
Warning: Removed 8 rows containing missing values (`geom_point()`).
# }
Map overall mean concentration values of selected taxa per
station

Map mean count values of selected taxa and specified dates
# # ggOceanMaps:
# https://biostats-r.github.io/biostats/workingInR/140_maps.html
# https://github.com/MikkoVihtakari/ggOceanMaps: Use this one: remotes::install_github("MikkoVihtakari/ggOceanMaps")
# install.packages("ggOceanMaps") # # This is outdated, don't use!!!
library(ggOceanMaps)
library(leaflet)
# For NOAA machines
# path_sfer_list <- "/Users/enrique.montes/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/proposals/2022_02_MultiStressor_NOAA/module_1"
# For personal machine
path_sfer_list <- "~/Library/CloudStorage/GoogleDrive-enriquemontes01@gmail.com/My Drive/GDrive/proposals/2022_02_MultiStressor_NOAA/module_1"
sfer_curated <- list.files(path_sfer_list, pattern = "sfer_stations_curated.csv", full.names = TRUE)
sfer_sta_list <- read.csv(sfer_curated, header = TRUE)
merged_data <- taxa_meta %>%
left_join(sfer_sta_list %>% filter(station_class %in% "C"), by = c('Station' = 'station_id'))
# Replace 2023 and 10 with your selected year and month
selected_year <- 2023
selected_month <- 11
# Filter rows
filtered_taxa_meta <- merged_data %>%
filter(year == selected_year, month == selected_month)
# filtered_taxa_meta <- merged_data
# Compute weighted average lat lons based on selected variable, and mean variable values (e.g., Copepods)
taxa_avg <- filtered_taxa_meta %>%
group_by(line_id) %>%
mutate(weight = Guinardia / sum(Guinardia, na.rm = TRUE)) %>%
summarise(longitude = weighted.mean(dec_lon, w = weight, na.rm = TRUE),
latitude = weighted.mean(dec_lat, w = weight, na.rm = TRUE),
sel_taxa_mean = mean(Guinardia, na.rm = TRUE))
# # Filter out values less than 1
filtered_taxa_avg <- taxa_avg %>%
filter(sel_taxa_mean > 0)
# Find the minimum and maximum values of sel_taxa_mean
min_value <- min(filtered_taxa_avg$sel_taxa_mean, na.rm = TRUE)
max_value <- max(filtered_taxa_avg$sel_taxa_mean, na.rm = TRUE)
# # Adjust the color palette with specified values
# color_palette <- colorNumeric(palette = "Oranges", domain = c(min_value, max_value))
# # Create the map
# map <- leaflet(filtered_taxa_avg) %>%
# addProviderTiles("Esri.WorldImagery") %>%
# addCircleMarkers(
# lng = ~longitude,
# lat = ~latitude,
# radius = 10,
# color = ~color_palette(sel_taxa_mean),
# fillOpacity = 0.8,
# popup = ~paste("Line ID: ", line_id, "<br>Copepods: ", sel_taxa_mean)
# ) %>%
# addLegend(
# position = "bottomright",
# pal = color_palette,
# values = c(min_value, max_value), # Adjusted values here
# title = "Copepod occurrences",
# opacity = 1
# )
# map
# # Map with bathymetry
# Overlay color-scaled dots
dt <- data.frame(
lon = filtered_taxa_avg$longitude,
lat = filtered_taxa_avg$latitude,
sel_param = filtered_taxa_avg$sel_taxa_mean)
# # Colors:
# Acantharea = darkturquoise; breaks = c(0.25, 1, 2.5, 5); limits = c(0.1, 10)
# Copepods = red; breaks = c(0.25, 1, 2.5, 5, 10); limits = c(0.1, 15)
# Chain diatoms = orange; breaks = c(0.5, 1, 10, 30, 60); c(0.1, 70)
# Chaetoceros = purple; breaks = c(0.25, 1, 2.5, 5, 10); limits = c(0.1, 15)
# Echinoderms = magenta; breaks = c(0.5, 1, 3, 5); limits = c(0.1, 5)
# Larvaceans = plum; breaks = c(0.5, 1, 3, 5); limits = c(0.1, 5)
# Polychaetes = dodgerblue; breaks = c(0.25, 0.5, 1, 3); limits = c(0.1, 3)
# Jellies = slategray4; breaks = c(0.25, 0.5, 1, 2); limits = c(0.1, 3)
occ_map <- basemap(limits = c(-84, -79.5, 24, 28.5), bathymetry = TRUE) +
geom_point(data = dt, aes(x = lon, y = lat, size = sel_param), fill = "olivedrab2", color = "olivedrab2") +
geom_point(data = filtered_taxa_meta, aes(x = dec_lon, y = dec_lat), color = "black", shape = 3, size = 1.5) +
scale_size_continuous(name = "Average counts",
breaks = c(0.5, 1, 3, 5, 10),
limits = c(0.1, 40),
range = c(1, 15)) +
theme_minimal()
occ_map
# occ_map_leg <- ggplot(dt, aes(lon, lat)) +
# geom_point(aes(size = sel_param), fill = "green", color ="green") +
# theme_minimal()
# occ_map_leg
Create time-series plots of selected taxa at specific stations
selected_variable <- 'Chaetoceros'
# # Lower Keys
# selected_line_id <- c('LK','WS','MO','KW')
# # Middle Keys
# selected_line_id <- c('CO', 'CR')
# # Other regions
selected_line_id <- c('CAL')
# Filter the data for the selected line_id
filtered_merged_data <- merged_data[merged_data$line_id %in% selected_line_id, ]
# Remove rows with NAs in relevant columns
monthly_means <- na.omit(filtered_merged_data[, c("date", selected_variable, "line_id")]) %>%
group_by(year_month = format(date, "%Y-%m")) %>%
summarise(mean_value = mean(!!sym(selected_variable), na.rm = TRUE),
se_value = sd(!!sym(selected_variable), na.rm = TRUE) / sqrt(n()))
# Create a time series plot
ggplot(monthly_means, aes(x = year_month, y = mean_value)) +
geom_bar(stat = "identity", fill = "orange") +
geom_errorbar(aes(ymin = mean_value - se_value, ymax = mean_value + se_value),
width = 0.2, # Adjust width as needed
position = position_dodge(width = 0.8)) + # Adjust width as needed
labs(x = "Year-Month", y = "Mean Value") +
ggtitle("Monthly Means of Selected Variable") +
theme_minimal()
Match file name with string ID - DO NOT USE
# library(tidyverse)
# library(lubridate)
# library(dplyr)
#
# # extract relevant part of the strings
# df <- rbind(class.Copepods,
# class.Eucampia,
# class.Noctiluca,
# class.Polychaets,
# class.Acantharea,
# class.Centric,
# class.Ceratium,
# class.Chaetoceros,
# class.Chain2,
# class.Chain3,
# class.Chain4,
# class.Ostracods,
# class.Jellies,
# class.Larvaceans,
# class.pellets)
# sub_strings <- substr(df$V1, start = 10, stop = 22)
# unique_all <- unique(sub_strings)
#
# # select unique dates (this allows to search CTD records per date and time)
# # To find unique dates and times to extract CDT data use: unique_all[grepl("20221209", unique_all)]
#
# id_list <- unique_all
# id_list2 <- as.POSIXct(id_list, format="%Y%m%d_%H%M", tz="UTC")
#
# conc_occ_count <- data.frame(date = as.Date(character()), stringsAsFactors = FALSE)
#
# for ( i in seq_along(id_list)){
# acantha <- as.data.frame(str_count(class.Acantharea$V1, id_list[i]))
# centric <- as.data.frame(str_count(class.Centric$V1, id_list[i]))
# ceratium <- as.data.frame(str_count(class.Ceratium$V1, id_list[i]))
# chaetoceros <- as.data.frame(str_count(class.Chaetoceros$V1, id_list[i]))
# chaetog <- as.data.frame(str_count(class.Chaetognaths$V1, id_list[i]))
# chain2 <- as.data.frame(str_count(class.Chain2$V1, id_list[i]))
# chain3 <- as.data.frame(str_count(class.Chain3$V1, id_list[i]))
# chain4 <- as.data.frame(str_count(class.Chain4$V1, id_list[i]))
# ostra <- as.data.frame(str_count(class.Ostracods$V1, id_list[i]))
# copepods <- as.data.frame(str_count(class.Copepods$V1, id_list[i]))
# decapod <- as.data.frame(str_count(class.Decapods$V1, id_list[i]))
# echino <- as.data.frame(str_count(class.Echinoderms$V1, id_list[i]))
# eucampia <- as.data.frame(str_count(class.Eucampia$V1, id_list[i]))
# jellies <- as.data.frame(str_count(class.Jellies$V1, id_list[i]))
# larvae <- as.data.frame(str_count(class.Larvaceans$V1, id_list[i]))
# nocti <- as.data.frame(str_count(class.Noctiluca$V1, id_list[i]))
# polychaetes <- as.data.frame(str_count(class.Polychaets$V1, id_list[i]))
# tricho <- as.data.frame(str_count(class.Tricho$V1, id_list[i]))
#
# Acantharea <- colSums(acantha != 0)
# Centric <- colSums(centric != 0)
# Ceratium_spp <- colSums(ceratium != 0)
# Chaetoceros <- colSums(chaetoceros != 0)
# Chaetognaths <- colSums(chaetog != 0)
# Diatom_chains_1 <- colSums(chain2 != 0)
# Diatom_chains_2 <- colSums(chain3 != 0)
# Diatom_chains_3 <- colSums(chain4 != 0)
# Ostracods <- colSums(ostra != 0)
# Copepods <- colSums(copepods != 0)
# Decapods <- colSums(decapod != 0)
# Echinoderms <- colSums(echino != 0)
# Eucampia_spp <- colSums(eucampia != 0)
# Jellies<- colSums(jellies != 0)
# Larvaceans <- colSums(larvae != 0)
# Noctiluca <- colSums(nocti != 0)
# Polychaetes <- colSums(polychaetes != 0)
# Trichodesmium_spp <- colSums(tricho != 0)
#
# # Parse the date-time string with ymd_hm()
# occ_datetime <- as.POSIXct(id_list[i], format="%Y%m%d_%H%M", tz="UTC")
# occ_datetime_str <- substr(id_list[i], 1, 13)
#
# row_df <- data.frame(date = occ_datetime, occ_datetime_str,
# Acantharea,
# Centric,
# Ceratium_spp,
# Chaetoceros,
# Chaetognaths,
# Diatom_chains_1,
# Diatom_chains_2,
# Diatom_chains_3,
# Ostracods,
# Copepods,
# Decapods,
# Echinoderms,
# Eucampia_spp,
# Jellies,
# Larvaceans,
# Noctiluca,
# Polychaetes,
# Trichodesmium_spp
# )
# rownames(row_df) <- i
#
# conc_occ_count <- rbind(conc_occ_count, row_df)
# }
#
# conc_occ_final <- arrange(conc_occ_count, date)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vazogU3RyaW5nbWF0Y2giCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCgojIExvYWQgZGF0YSBmaWxlcwpgYGB7cn0KbGlicmFyeShkcGx5cikKCiMgc3BlY2lmeSB0aGUgZGlyZWN0b3J5IHdoZXJlIHRoZSBmaWxlcyBhcmUgbG9jYXRlZAojIEZvciBOT0FBIG1hY2hpbmVzCiMgZGlyX3BhdGggPC0gIi9Vc2Vycy9lbnJpcXVlLm1vbnRlcy9Hb29nbGUgRHJpdmUvTXkgRHJpdmUvR0RyaXZlL09DRURfQU9NTC9XU19jcnVpc2VzL3BsYW5rdG9uX2ltYWdpbmcvQ1BJQ1MvVFMuTWFzdGVyX3NlbGVjdGlvbiIKIyBGb3IgcGVyc29uYWwgbWFjaGluZQpkaXJfcGF0aCA8LSAifi9MaWJyYXJ5L0Nsb3VkU3RvcmFnZS9Hb29nbGVEcml2ZS1lbnJpcXVlbW9udGVzMDFAZ21haWwuY29tL015IERyaXZlL0dEcml2ZS9PQ0VEX0FPTUwvV1NfY3J1aXNlcy9wbGFua3Rvbl9pbWFnaW5nL0NQSUNTL1RTLk1hc3Rlcl9zZWxlY3Rpb24iCgojIG9idGFpbiBhIGxpc3Qgb2YgZmlsZSBuYW1lcyBpbiB0aGUgZGlyZWN0b3J5CmZpbGVfbmFtZXMgPC0gbGlzdC5maWxlcyhwYXRoID0gZGlyX3BhdGgsIHBhdHRlcm4gPSAiLnR4dCIsIGZ1bGwubmFtZXMgPSBUUlVFKQoKIyBsb29wIG92ZXIgZWFjaCBmaWxlIGFuZCBpbXBvcnQgdGhlIHRhYmxlcyAodXNlIHRoaXMgZm9yIERBVEVTKQpmb3IgKGZpbGUgaW4gZmlsZV9uYW1lcykgewogIHRhYmxlX25hbWUgPC0gZ3N1YigiLnR4dCIsICIiLCBiYXNlbmFtZShmaWxlKSkgIyBnZXQgdGhlIG5hbWUgb2YgdGhlIHRhYmxlIGZyb20gdGhlIGZpbGUgbmFtZQogIGFzc2lnbih0YWJsZV9uYW1lLCByZWFkLnRhYmxlKGZpbGUgPSBmaWxlLCBoZWFkZXIgPSBGQUxTRSwgc2VwID0gIlx0IikgJT4lCiAgICAgICAgICAgbXV0YXRlKGRhdGUgPSBhcy5QT1NJWGN0KHN1YnN0cihWMSwgc3RhcnQgPSAyNCwgc3RvcCA9IDM2KSwgZm9ybWF0PSIlWSVtJWRfJUglTSIsIHR6PSJVVEMiKSkpCn0KCiMgIyBsb29wIG92ZXIgZWFjaCBmaWxlIGFuZCBpbXBvcnQgdGhlIHRhYmxlcyAodXNlIHRoaXMgZm9yIFNUUklOR1MpCiMgZm9yIChmaWxlIGluIGZpbGVfbmFtZXMpIHsKIyAgIHRhYmxlX25hbWUgPC0gZ3N1YigiLnR4dCIsICIiLCBiYXNlbmFtZShmaWxlKSkgIyBnZXQgdGhlIG5hbWUgb2YgdGhlIHRhYmxlIGZyb20gdGhlIGZpbGUgbmFtZQojICAgYXNzaWduKHRhYmxlX25hbWUsIHJlYWQudGFibGUoZmlsZSA9IGZpbGUsIGhlYWRlciA9IEZBTFNFLCBzZXAgPSAiXHQiKSkKIyB9CgpgYGAKCgojIE1hdGNoIGZpbGUgbmFtZSB1c2luZyBEQVRFIHZhbHVlcwpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobHVicmlkYXRlKQoKZGlyX3BhdGgyIDwtICJ+L0xpYnJhcnkvQ2xvdWRTdG9yYWdlL0dvb2dsZURyaXZlLWVucmlxdWVtb250ZXMwMUBnbWFpbC5jb20vTXkgRHJpdmUvR0RyaXZlL09DRURfQU9NTC9XU19jcnVpc2VzL3BsYW5rdG9uX2ltYWdpbmcvQ1BJQ1Mvd3NfY3J1aXNlX2N0ZCIKCmZpbGVfbmFtZSA8LSBsaXN0LmZpbGVzKHBhdGggPSBkaXJfcGF0aDIsIHBhdHRlcm4gPSAiY3RkX21ldGFfdjMuY3N2IiwgZnVsbC5uYW1lcyA9IFRSVUUpCmN0ZF9tZXRhIDwtIHJlYWQuY3N2KGZpbGVfbmFtZSwgZmlsbCA9IFRSVUUpCgojIFVTRSBXSVRIIGN0ZF9tZXRhX3YzLmNzdgpkdF9saXN0IDwtIGFzLlBPU0lYY3QocGFzdGUoY3RkX21ldGEkeWVhciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwcmludGYoIiUwMmQiLCBjdGRfbWV0YSRtb250aCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcHJpbnRmKCIlMDJkIiwgY3RkX21ldGEkZGF5KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN0ZF9tZXRhJHRpbWVfZ210KSwKICAgICAgICAgICAgICAgICAgICAgIGZvcm1hdCA9ICIlWSVtJWQgJUk6JU06JVMgJXAiLAogICAgICAgICAgICAgICAgICAgICAgdHogPSAiVVRDIikKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBUaGlzIHNlY3Rpb24gZGV0ZWN0cyBzaG9ydCB0cmFuc2l0IHRpbWVzIGJldHdlZW4gc3RhdGlvbnMKCiMgQ2FsY3VsYXRlIHRpbWUgZGlmZmVyZW5jZXMgaW4gc2Vjb25kcyBiZXR3ZWVuIGNvbnNlY3V0aXZlIGR0X2xpc3Qgb2JqZWN0cwp0aW1lX2RpZmZlcmVuY2VzIDwtIGFzLm51bWVyaWMoZGlmZnRpbWUoZHRfbGlzdFstMV0sIGR0X2xpc3RbLWxlbmd0aChkdF9saXN0KV0sIHVuaXRzID0gInNlY3MiKSkKCiMgQ29udmVydCB0aW1lIGRpZmZlcmVuY2VzIGZyb20gc2Vjb25kcyB0byBtaW51dGVzCnRpbWVfZGlmZmVyZW5jZXNfbWlucyA8LSB0aW1lX2RpZmZlcmVuY2VzIC8gNjAKCiMgQ3JlYXRlIGEgZGF0YSBmcmFtZSBzaG93aW5nIHRoZSBvcmlnaW5hbCB0aW1lcyBhbmQgdGhlaXIgZGlmZmVyZW5jZXMgaW4gbWludXRlcwp0aW1lX2RpZmZfZGYgPC0gZGF0YS5mcmFtZSgKICBzdGFydF90aW1lID0gZHRfbGlzdFstbGVuZ3RoKGR0X2xpc3QpXSwKICBlbmRfdGltZSA9IGR0X2xpc3RbLTFdLAogIHRpbWVfZGlmZmVyZW5jZV9taW5zID0gdGltZV9kaWZmZXJlbmNlc19taW5zCikKCiMgZmluZCBDVEQgdGltZSBzdGFtcHMgb2YgY29uc2VjdXRpdmUgc3RhdGlvbnMgd2l0aGluIGxlc3MgdGhhbiAyMCBtaW4uIFRoaXMgd2lsbCBpZGVudGlmeSBDVEQgY2FzdHMgdGhhdCBhcmUgY2xvc2UgdG8gZWFjaCBvdGhlcgpzaG9ydF90X2lkeCA8LSB3aGljaCh0aW1lX2RpZmZfZGYkdGltZV9kaWZmZXJlbmNlX21pbnMgPCAyMCkKc2hvcnRfdGltZXN0YW1wcyA8LSBkdF9saXN0W3Nob3J0X3RfaWR4XQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKIyBDcmVhdGUgZW1wdHkgZGF0YSBmcmFtZSB0byBzdG9yZSByZXN1bHRzCmNvbmNfb2NjX2ZpbmFsIDwtIGRhdGEuZnJhbWUoZGF0ZSA9IGNoYXJhY3RlcigpLCBjb3VudCA9IG51bWVyaWMoKSkKCiMgTGlzdCBvZiBjbGFzcyBvYmplY3RzIHRvIGJlIHByb2Nlc3NlZApjbGFzc19uYW1lcyA8LSBjKCJBY2FudGhhcmVhIiwgIkNlbnRyaWMiLCAiQ2VyYXRpdW0iLCAiQ2hhZXRvY2Vyb3MiLCAiQ2hhZXRvZ25hdGhzIiwgCiAgICAgICAgICAgICAgICAgIkNoYWluMiIsICJDaGFpbjMiLCAiT3N0cmFjb2RzIiwgIkNvcGVwb2RzIiwgIkRlY2Fwb2RzIiwgIkVjaGlub2Rlcm1zIiwgCiAgICAgICAgICAgICAgICAgIkd1aW5hcmRpYSIsICJKZWxsaWVzIiwgIkxhcnZhY2VhbnMiLCAiTmVvY2FseXB0cmVsbGEiLCAiTm9jdGlsdWNhIiwgCiAgICAgICAgICAgICAgICAgIlBvbHljaGFldHMiLCAiUHRlcm9wb2RzIiwgIlRyaWNobyIpCgojIHRpbWUgYnVmZmVyIGJlZm9yZSBhbmQgYWZ0ZXIgQ1REIHRpbWUgaW4gc2Vjb25kcyBzbyB0aGF0IENQSUNTIHJlY29yZHMgYXJlIG1hdGNoZWQgdG8gQ1REIHRpbWVzLgpzdGFydCA8LSAxMCAqIDYwIApzdG9wIDwtIDEwICogNjAgCgojIEl0ZXJhdGUgb3ZlciBkdF9saXN0IGludGVydmFscwpmb3IgKGkgaW4gMTpsZW5ndGgoZHRfbGlzdCkpIHsKICAKICAjIEluaXRpYWxpemUgYSBsaXN0IHRvIHN0b3JlIGNvdW50cyBmb3IgdGhlIGN1cnJlbnQgaW50ZXJ2YWwKICBjb3VudHNfbGlzdCA8LSBsaXN0KGRhdGUgPSBkdF9saXN0W2ldKQogIAogICMgSXRlcmF0ZSBvdmVyIGVhY2ggY2xhc3Mgb2JqZWN0IGFuZCBwZXJmb3JtIHN1YnNldHRpbmcKICBmb3IgKGNsYXNzX25hbWUgaW4gY2xhc3NfbmFtZXMpIHsKICAgIGNsYXNzX2RhdGEgPC0gZ2V0KHBhc3RlMCgiY2xhc3MuIiwgY2xhc3NfbmFtZSkpICAjIER5bmFtaWNhbGx5IGdldCB0aGUgY2xhc3MgZGF0YSBmcmFtZQogICAgCiAgICBpZiAoaSA8IGxlbmd0aChkdF9saXN0KSkgewogICAgICAjIFN1YnNldHRpbmcgZm9yIGFsbCBpbnRlcnZhbHMgZXhjZXB0IHRoZSBsYXN0IG9uZQogICAgICBzdWJzZXRfZGF0YSA8LSBzdWJzZXQoY2xhc3NfZGF0YSwgZGF0ZSA+PSBkdF9saXN0W2ldLXN0YXJ0ICYgZGF0ZSA8IGR0X2xpc3RbaSsxXS1zdG9wKQogICAgfSBlbHNlIHsKICAgICAgIyBTdWJzZXR0aW5nIGZvciB0aGUgbGFzdCBpbnRlcnZhbDogY2FwdHVyZSBhbGwgZGF0YSBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gdGhlIGxhc3QgZHRfbGlzdAogICAgICBzdWJzZXRfZGF0YSA8LSBzdWJzZXQoY2xhc3NfZGF0YSwgZGF0ZSA+PSBkdF9saXN0W2ldLXN0YXJ0KQogICAgfQogICAgCiAgICBjb3VudHNfbGlzdFtbY2xhc3NfbmFtZV1dIDwtIG5yb3coc3Vic2V0X2RhdGEpCiAgfQogIAogICMgQ29udmVydCBjb3VudHNfbGlzdCB0byBhIGRhdGEgZnJhbWUgYW5kIGJpbmQgaXQgdG8gdGhlIHJlc3VsdAogIHJlc3VsdCA8LSBhcy5kYXRhLmZyYW1lKGNvdW50c19saXN0KQogIGNvbmNfb2NjX2ZpbmFsIDwtIHJiaW5kKGNvbmNfb2NjX2ZpbmFsLCByZXN1bHQpCn0gCgojIENvbWJpbmUgd2l0aCBjdGRfbWV0YQp0YXhhX21ldGEgPC0gY2JpbmQoY3RkX21ldGEsIGNvbmNfb2NjX2ZpbmFsKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDaGVjayBmb3IgdW5hY2NvdW50ZWQgQ1BJQ1MgcmVjb3JkcyAKCiMgSW5pdGlhbGl6ZSBhIGxpc3QgdG8gc3RvcmUgdW5hY2NvdW50ZWQgZGF0ZXMgZm9yIGVhY2ggY2xhc3MKdW5hY2NvdW50ZWRfZGF0ZXNfbGlzdCA8LSBsaXN0KCkKCiMgSXRlcmF0ZSBvdmVyIGVhY2ggY2xhc3Mgb2JqZWN0CmZvciAoY2xhc3NfbmFtZSBpbiBjbGFzc19uYW1lcykgewogICMgR2V0IHRoZSBkYXRlLXRpbWUgb2JqZWN0cyBmcm9tIHRoZSBjdXJyZW50IGNsYXNzCiAgc2VsX2NsYXNzX2RhdGVzIDwtIGdldChwYXN0ZTAoImNsYXNzLiIsIGNsYXNzX25hbWUpKSRkYXRlCiAgCiAgIyBJbml0aWFsaXplIGEgbG9naWNhbCB2ZWN0b3IgdG8gdHJhY2sgd2hldGhlciBlYWNoIGNsYXNzIGRhdGUgaXMgYWNjb3VudGVkIGZvcgogIGlzX2FjY291bnRlZF9mb3IgPC0gcmVwKEZBTFNFLCBsZW5ndGgoc2VsX2NsYXNzX2RhdGVzKSkKICAKICAjIENoZWNrIGVhY2ggY2xhc3MgZGF0ZSBhZ2FpbnN0IHRoZSBpbnRlcnZhbHMgaW4gZHRfbGlzdAogIGZvciAoaSBpbiAxOmxlbmd0aChkdF9saXN0KSkgewogICAgaWYgKGkgPCBsZW5ndGgoZHRfbGlzdCkpIHsKICAgICAgIyBDaGVjayBhbGwgaW50ZXJ2YWxzIGV4Y2VwdCB0aGUgbGFzdCBvbmUKICAgICAgaW50ZXJ2YWxfc3RhcnQgPC0gZHRfbGlzdFtpXSAtIHN0YXJ0CiAgICAgIGludGVydmFsX2VuZCA8LSBkdF9saXN0W2kgKyAxXSAtIHN0b3AKICAgIH0gZWxzZSB7CiAgICAgICMgTGFzdCBpbnRlcnZhbCBjYXB0dXJlcyBhbGwgZGF0YSBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gdGhlIGxhc3QgZHRfbGlzdAogICAgICBpbnRlcnZhbF9zdGFydCA8LSBkdF9saXN0W2ldIC0gc3RhcnQKICAgICAgaW50ZXJ2YWxfZW5kIDwtIEluZiAgIyBFZmZlY3RpdmVseSBubyB1cHBlciBib3VuZAogICAgfQogICAgCiAgICAjIE1hcmsgY2xhc3MgZGF0ZXMgdGhhdCBmYWxsIHdpdGhpbiB0aGUgY3VycmVudCBpbnRlcnZhbCBhcyBhY2NvdW50ZWQgZm9yCiAgICBpc19hY2NvdW50ZWRfZm9yIDwtIGlzX2FjY291bnRlZF9mb3IgfCAoc2VsX2NsYXNzX2RhdGVzID49IGludGVydmFsX3N0YXJ0ICYgc2VsX2NsYXNzX2RhdGVzIDwgaW50ZXJ2YWxfZW5kKQogIH0KICAKICAjIFN1YnNldCB0aGUgY2xhc3MgZGF0ZXMgdGhhdCB3ZXJlIG5vdCBhY2NvdW50ZWQgZm9yCiAgdW5hY2NvdW50ZWRfc2VsX2NsYXNzX2RhdGVzIDwtIHNlbF9jbGFzc19kYXRlc1shaXNfYWNjb3VudGVkX2Zvcl0KICAKICAjIFN0b3JlIHRoZSB1bmFjY291bnRlZCBkYXRlcyBpbiB0aGUgbGlzdAogIHVuYWNjb3VudGVkX2RhdGVzX2xpc3RbW2NsYXNzX25hbWVdXSA8LSB1bmFjY291bnRlZF9zZWxfY2xhc3NfZGF0ZXMKfQoKIyBQcmludCBvciB2aWV3IHRoZSB1bmFjY291bnRlZCBkYXRlcyBmb3IgZWFjaCBjbGFzcwpmb3IgKGNsYXNzX25hbWUgaW4gY2xhc3NfbmFtZXMpIHsKICBjYXQoIlVuYWNjb3VudGVkIGRhdGVzIGZvciBjbGFzczoiLCBjbGFzc19uYW1lLCAiXG4iKQogIHByaW50KHVuYWNjb3VudGVkX2RhdGVzX2xpc3RbW2NsYXNzX25hbWVdXSkKICBjYXQoIlxuIikKfQpgYGAKCiMgIyBDYWxjdWxhdGUgcGxhbmt0b24gY29uY2VudHJhdGlvbiB0aW1lIHNlcmllcyBwZXIgc3RhdGlvbgpgYGB7cn0KIyBDYWxjdWxhdGUgc3BlY2llcyBjb25jZW50cmF0aW9uIChjb3VudHMvbWwpIGZvciBlYWNoIHNwZWNpZXMKCiMgIyBGb3Igem9vcGxhbmt0b24KIyB0YXhhX21ldGFfY29uY2VudHJhdGlvbiA8LSB0YXhhX21ldGEgJT4lCiMgICBtdXRhdGUoYWNyb3NzKGMoQWNhbnRoYXJlYSxDaGFldG9nbmF0aHMsT3N0cmFjb2RzLENvcGVwb2RzLERlY2Fwb2RzLEVjaGlub2Rlcm1zLEplbGxpZXMsCiMgICAgIExhcnZhY2VhbnMsUG9seWNoYWV0cyxQdGVyb3BvZHMpLCB+IC4vdG90YWxfdm9sX3NhbXBsZWQgKiAxZTYpKSAlPiUKIyAgIHNlbGVjdChTdGF0aW9uLCBkZWNfbGF0LCBkZWNfbG9uLCB5ZWFyLCBtb250aCwgZGF0ZSwgQXZnLmNobC5hLi51Zy5MLiwKIyAgICAgICAgICB0ZW1wLi5kZWdDLiwgc2FsaW5pdHksIHRvdGFsX3ZvbF9zYW1wbGVkLAojICAgICAgICAgIEFjYW50aGFyZWEsQ2hhZXRvZ25hdGhzLE9zdHJhY29kcyxDb3BlcG9kcyxEZWNhcG9kcyxFY2hpbm9kZXJtcywKIyAgICAgICAgICBKZWxsaWVzLExhcnZhY2VhbnMsUG9seWNoYWV0cyxQdGVyb3BvZHMpICU+JQojICAgZmlsdGVyKCFpcy5uYSh0b3RhbF92b2xfc2FtcGxlZCkpCiMgCiMgIyAjIFRyYW5zZm9ybSB0YXhhX21ldGFfY29uY2VudHJhdGlvbiB0byBsb25nIGZvcm1hdAojIHRheGFfbWV0YV9sb25nIDwtIHRheGFfbWV0YV9jb25jZW50cmF0aW9uICU+JQojICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKEFjYW50aGFyZWEsIENoYWV0b2duYXRocyxPc3RyYWNvZHMsQ29wZXBvZHMsRGVjYXBvZHMsCiMgICAgICAgICAgICAgICAgICAgICAgICAgRWNoaW5vZGVybXMsIEplbGxpZXMsIExhcnZhY2VhbnMsUG9seWNoYWV0cywgUHRlcm9wb2RzKSwKIyAgICAgICAgICAgICAgICBuYW1lc190byA9ICJzcGVjaWVzIiwgdmFsdWVzX3RvID0gInNwZWNpZXNfY29uY2VudHJhdGlvbiIpCgojIEZvciBwaHl0b3BsYW5rdG9uCnRheGFfbWV0YV9jb25jZW50cmF0aW9uIDwtIHRheGFfbWV0YSAlPiUKICBtdXRhdGUoYWNyb3NzKGMoQ2VudHJpYyxDZXJhdGl1bSxDaGFldG9jZXJvcyxDaGFpbjIsQ2hhaW4zLEd1aW5hcmRpYSxOZW9jYWx5cHRyZWxsYSwKICAgICAgICAgICAgICAgICAgTm9jdGlsdWNhLFRyaWNobyksIH4gLi90b3RhbF92b2xfc2FtcGxlZCAqIDFlNikpICU+JQogIHNlbGVjdChTdGF0aW9uLCBkZWNfbGF0LCBkZWNfbG9uLCB5ZWFyLCBtb250aCwgZGF0ZSwgQXZnLmNobC5hLi51Zy5MLiwKICAgICAgICAgdGVtcC4uZGVnQy4sIHNhbGluaXR5LHRvdGFsX3ZvbF9zYW1wbGVkLAogICAgICAgICBDZW50cmljLENlcmF0aXVtLENoYWV0b2Nlcm9zLENoYWluMixDaGFpbjMsR3VpbmFyZGlhLE5lb2NhbHlwdHJlbGxhLAogICAgICAgICAgICAgICAgICBOb2N0aWx1Y2EsVHJpY2hvKSAlPiUKICBmaWx0ZXIoIWlzLm5hKHRvdGFsX3ZvbF9zYW1wbGVkKSkKCiMgIyBUcmFuc2Zvcm0gdGF4YV9tZXRhX2NvbmNlbnRyYXRpb24gdG8gbG9uZyBmb3JtYXQKdGF4YV9tZXRhX2xvbmcgPC0gdGF4YV9tZXRhX2NvbmNlbnRyYXRpb24gJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKENlbnRyaWMsQ2VyYXRpdW0sQ2hhZXRvY2Vyb3MsQ2hhaW4yLENoYWluMyxHdWluYXJkaWEsTmVvY2FseXB0cmVsbGEsCiAgICAgICAgICAgICAgICAgIE5vY3RpbHVjYSxUcmljaG8pLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJzcGVjaWVzIiwgdmFsdWVzX3RvID0gInNwZWNpZXNfY29uY2VudHJhdGlvbiIpCgojIEZpbHRlciB0aGUgZGF0YSBmb3IgU3RhdGlvbnMKc3RhdGlvbl9saXN0X2ZrIDwtIGMoIldTIiwiMjEvTEsiLCJNUiIsIjE2IiwiMTgiLCIxMCIsIjEyIiwiOS41IiwiOSIsIjciLCIyIikKIyBzdGF0aW9uX2xpc3Rfc3IgPC0gYygiNjgiLCI2NSIsIjY0IiwiNjAiLCI1OCIsIjU3LjMiLCI1Ny4yIiwiNTcuMSIsIjU3IiwiNTYiLCI1NSIsIjU0IiwiNTMiLCI1MSIsCiMgICAgICAgICAgICAgICAgICAgIjQ5IiwiNDciLCI0NSIsIjQxIiwiMzAiLCIzMSIsIjMzIikKIyBzdGF0aW9uX2xpc3RfY2FsIDwtIGMoIkNBTDUiLCJDQUw0IiwiQ0FMMyIsIkNBTDIiLCJDQUwxIiwiUlAxIiwiUlAyIiwiUlAzIiwiUlA0IiwiR1A1IiwiQkc0IiwiQkczIiwKIyAgICAgICAgICAgICAgICAgICAiQkcyIiwiQkcxIiwiQkc2IiwgIkJHNyIpCiMgc3RhdGlvbl9saXN0X3ZsIDwtIGMoIlYxIiwiVjIiLCJWMyIsIlY0IiwiVjUiLCJWNiIsIlY3IiwiVjgiLCJWOSIsIkwxIiwiTDMiLCJMNSIsIkw3IiwiTDkiKQojIHN0YXRpb25fbGlzdF90YiA8LSBjKCJBTUk5IiwiQU1JOCIsIkFNSTciLCJBTUk2IiwiQU1JNSIsIkFNSTQiLCJBTUkzIiwiQU1JMiIsIkFNSTEiLCJUQjEiLCJUQjIiLAojICAgICAgICAgICAgICAgICAgICJUQjMiLCJUQjQiLCJUQjUiLCJUQjEwIiwiQ1c0IiwiQ1czIiwiQ1cyIiwiQ1cxIikKZmlsdGVyZWRfdGF4YV9tZXRhX2xvbmcgPC0gdGF4YV9tZXRhX2xvbmcgJT4lCiAgZmlsdGVyKFN0YXRpb24gJWluJSBzdGF0aW9uX2xpc3RfZmspCgojIFdpdGhvdXQgZmlsdGVyaW5nIHBlciBncm91cCBvZiBzdGF0aW9ucyBidXQgbG9va2luZyBhdCB0aGUgZW50aXJlIHJlZ2lvbgojIGZpbHRlcmVkX3RheGFfbWV0YV9sb25nIDwtIHRheGFfbWV0YV9sb25nCgojIENhbGN1bGF0ZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gb2Ygc3BlY2llcyBjb25jZW50cmF0aW9uIGZvciBlYWNoIGRhdGUKc3VtbWFyeV9kYXRhIDwtIGZpbHRlcmVkX3RheGFfbWV0YV9sb25nICU+JQogIGdyb3VwX2J5KHllYXIsIG1vbnRoLCBzcGVjaWVzKSAlPiUKICBzdW1tYXJpc2UobWVhbl9jb25jZW50cmF0aW9uID0gbWVhbihzcGVjaWVzX2NvbmNlbnRyYXRpb24pLAogICAgICAgICAgICBzZF9jb25jZW50cmF0aW9uID0gc2Qoc3BlY2llc19jb25jZW50cmF0aW9uKSkKCiMgQ29tYmluZSB5ZWFyIGFuZCBtb250aCBjb2x1bW5zIGludG8gYSBzaW5nbGUgZGF0ZSBjb2x1bW4Kc3VtbWFyeV9kYXRhJGRhdGUgPC0gYXMuRGF0ZShwYXN0ZShzdW1tYXJ5X2RhdGEkeWVhciwgc3VtbWFyeV9kYXRhJG1vbnRoLCAiMTUiLCBzZXAgPSAiLSIpKQoKIyBjdXN0b21fcGFsX2hleDIgPC0gYygnI2U0MWExYycsJyMzNzdlYjgnLCcjNGRhZjRhJywnIzk4NGVhMycsJyNmZjdmMDAnLCcjZmZmZjMzJywnI2E2NTYyOCcsJyNmNzgxYmYnKQpjdXN0b21fcGFsX2hleDIgPC0gYygnZGFya3R1cnF1b2lzZScsJ3JlZCcsJyM0ZGFmNGEnLCdzbGF0ZWdyYXk0Jywnb3JhbmdlJywnZG9kZ2VyYmx1ZScsJ3B1cnBsZScsICJ5ZWxsb3ciLCJwaW5rIiwidmlvbGV0IikKCiMgQWNhbnRoYXJlYSA9IGRhcmt0dXJxdW9pc2UKIyBDb3BlcG9kcyA9IHJlZAojIGVjaGlub3MgPSAjNGRhZjRhCiMgSmVsbGllcyA9IHNsYXRlZ3JheTQKIyBMYXJ2YWNlYW5zID0gb3JhbmdlCiMgUG9seWNoYWV0ZXMgPSBkb2RnZXJibHVlCiMgUHRlcm9wb2RzID0gcHVycGxlCgojICMgQ2hlY2sgZG9taW5hbmNlIG9mIGdyb3VwcyBieSBjYWxjdWxhdGluZyB0aGUgb3ZlcmFsbCBtZWFuIAojIHRlc3QgPC0gc3VtbWFyeV9kYXRhICU+JSBmaWx0ZXIoc3BlY2llcyA9PSAiUG9seWNoYWV0ZXMiKSAlPiUgc3VtbWFyaXplKGF2ZyA9IG1lYW4obWVhbl9jb25jZW50cmF0aW9uKSkKIyBtZWFuKHRlc3QkYXZnKQoKIyBQbG90IHRpbWUgc2VyaWVzIG9mIG1lYW4rc2QgcGxhbnRvbiBjb25jZW50cmF0aW9uIHBlciBncm91cCBvZiBzaXRlcwp6eiA8LSBnZ3Bsb3Qoc3VtbWFyeV9kYXRhLCBhZXMoeCA9IGRhdGUsIHkgPSBtZWFuX2NvbmNlbnRyYXRpb24sIGZpbGwgPSBzcGVjaWVzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBhbHBoYSA9IDAuNykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9wYWxfaGV4MikgKwogICMgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IDAsIHltYXggPSBtZWFuX2NvbmNlbnRyYXRpb24gKyBzZF9jb25jZW50cmF0aW9uKSwKICAjICAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHdpZHRoID0gMC4yKSArCiAgbGFicyh4ID0gIkRhdGUiLCB5ID0gIk1lYW4gU3BlY2llcyBDb25jZW50cmF0aW9uIiwgZmlsbCA9ICJTcGVjaWVzIikgKwogIHRoZW1lX21pbmltYWwoKQp6egoKIyBGaWx0ZXIgc3VtbWFyeV9kYXRhIGZvciBzZWxlY3RlZCBvbmx5CiMgQWNhbnRoYXJlYSwgQ29wZXBvZHMsIEVjaGlub2Rlcm1zLCBKZWxsaWVzLCBMYXJ2YWNlYW5zLCBDaGFldG9nbmF0aHMsIFBvbHljaGFldGVzLCBQdGVyb3BvZHMKIyBDZW50cmljLENlcmF0aXVtLENoYWV0b2Nlcm9zLERpYXRvbXMsRGlhdG9tczIsR3VpbmFyZGlhLE5lb2NhbHlwdHJlbGxhLE5vY3RpbHVjYSxUcmljaG9kZXNtaXVtCnN1bW1hcnlfc2VsZWN0ZWQgPC0gZmlsdGVyZWRfdGF4YV9tZXRhX2xvbmcgJT4lCiAgZmlsdGVyKHNwZWNpZXMgPT0gIlRyaWNobyIpICU+JQogIG11dGF0ZShkYXRlID0gYXMuRGF0ZShwYXN0ZSh5ZWFyLCBtb250aCwgIjE1Iiwgc2VwID0gIi0iKSkpCgojIENyZWF0ZSB0aGUgdGltZSBzZXJpZXMgYm94cGxvdAogIHNlbF9ib3ggPC0gIGdncGxvdChzdW1tYXJ5X3NlbGVjdGVkLCBhZXMoeCA9IGFzLmZhY3RvcihkYXRlKSwgeSA9IHNwZWNpZXNfY29uY2VudHJhdGlvbikpICsKICAgIGdlb21fYm94cGxvdChmaWxsID0gIndoaXRlIiwgbm90Y2ggPSBGQUxTRSwgb3V0bGllci5zaGFwZSA9IE5BKSArCiAgICBnZW9tX3Zpb2xpbihmaWxsID0gImxpZ2h0Z3JleSIsIGNvbG9yID0gIk5BIiwgYWxwaGEgPSAwLjcpICsKICAgICMgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yID0gZmFjdG9yKFN0YXRpb24pLCBzaXplID0gdGVtcC4uZGVnQy4pLCBhbHBoYSA9IDAuOSkgKwogICAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjI1LCBhZXMoY29sb3IgPSBzYWxpbml0eSwgc2l6ZSA9IHRlbXAuLmRlZ0MuKSwgYWxwaGEgPSAwLjYpICsKICAgICMgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjI1LCBhZXMoY29sb3IgPSBzYWxpbml0eSksIHNpemUgPSAzLCBhbHBoYSA9IDAuNikgKwogICAgbGFicyh4ID0gIkRhdGUiLCB5ID0gZXhwcmVzc2lvbigiRGVuc2l0eSAob3JnLiBtIl4iLTMifiIpIikpICsKICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhuYW1lID0gIlNhbGluaXR5Iiwgb3B0aW9uID0gIm1ha28iKSArCiAgICBzY2FsZV9zaXplX2NvbnRpbnVvdXMobmFtZSA9ICJUZW1wZXJhdHVyZSAowrBDKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygxNSwgMzUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMTUsIDIwLCAyNSwgMzAsIDM1KSwKICAgICAgICAgICAgICAgICAgICAgICAgICByYW5nZSA9IGMoMSwgNCkpICsKICAgIHlsaW0oLTUsIDMwMDAwKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgIyB0aGVtZV9pcHN1bSgpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpIAogIHNlbF9ib3gKCiMgIyBQbG90IGNvbmNlbnRyYXRpb24gdGltZSBzZXJpZXMgZm9yIGFsbCBzZWxlY3RlZCBzaXRlcwojIGtrIDwtIGdncGxvdChzdW1tYXJ5X3NlbGVjdGVkLCBhZXMoeCA9IGRhdGUsIHkgPSBzcGVjaWVzX2NvbmNlbnRyYXRpb24sIHNoYXBlID0gZmFjdG9yKFN0YXRpb24pKSkgKwojICAgbGFicyh4ID0gIkRhdGUiLCB5ID0gIlNwZWNpZXMgY29uY2VudHJhdGlvbiAob3JnLm0tMykiKSArCiMgICBnZW9tX3BvaW50KGFlcyhzaXplID0gdGVtcC4uZGVnQy4pLCBmaWxsID0gImRhcmtncmV5IikgKyAjIG9yIGNvbG91ciA9IGZhY3RvcihTdGF0aW9uKQojICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMCwgMSwgMiwgMywgNCwgNSwgNikpCiMga2sKCiMga2sgPC0gZ2dwbG90KHN1bW1hcnlfc2VsZWN0ZWQsIGFlcyh4ID0gZGF0ZSwgeSA9IHNwZWNpZXNfY29uY2VudHJhdGlvbiwgY29sb3VyID0gZmFjdG9yKFN0YXRpb24pKSkgKwojICAgbGFicyh4ID0gIkRhdGUiLCB5ID0gIlNwZWNpZXMgY29uY2VudHJhdGlvbiAob3JnLm0tMykiKSArCiMgICBnZW9tX3BvaW50KGFlcyhzaXplID0gdGVtcC4uZGVnQy4pLCBhbHBoYSA9IDAuNykgKyAjIG9yIGNvbG91ciA9IGZhY3RvcihTdGF0aW9uKSArCiMgICAjIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gcG9seSh4LCA0KSwgc2UgPSBGQUxTRSkgKyAjIEFkZCBwb2x5bm9taWFsIGZpdCBsaW5lCiMgICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpCiMga2sKICAKIyAjIEZpbmQgdGhlIGluZGV4IG9mIHRoZSBsYXN0IHJvdyB3aXRoIGRhdGUgPT0gJzIwMjMtMDEtMTUnIGFuZCBhZGQgYSBkdW1teSByb3cgZm9yIG1pc3NpbmcgZGF0ZXMKIyBuZXdfcm93IDwtIHRpYmJsZShTdGF0aW9uID0gTkEsIGRlY19sYXQgPSBOQSwgZGVjX2xvbiA9IE5BLCB5ZWFyID0gTkEsCiMgICAgICAgICAgICAgICAgICAgbW9udGggPSBOQSwgZGF0ZSA9IGFzLkRhdGUoJzIwMjMtMDMtMTUnKSwgQXZnLmNobC5hLi51Zy5MLiA9IE5BLAojICAgICAgICAgICAgICAgICAgIHRlbXAuLmRlZ0MuID0gTkEsIHNhbGluaXR5ID0gTkEsIHNwZWNpZXMgPSBOQSwgc3BlY2llc19jb25jZW50cmF0aW9uID0gTkEpCiMgbGFzdF9pbmRleCA8LSBtYXgod2hpY2goc3VtbWFyeV9zZWxlY3RlZCRkYXRlID09IGFzLkRhdGUoJzIwMjMtMDEtMTUnKSkpCiMgc3VtbWFyeV9zZWxlY3RlZCA8LSBiaW5kX3Jvd3MoCiMgICAgIHN1bW1hcnlfc2VsZWN0ZWRbMTpsYXN0X2luZGV4LCBdLAojICAgICBuZXdfcm93LAojICAgICBzdW1tYXJ5X3NlbGVjdGVkWyhsYXN0X2luZGV4ICsgMSk6bnJvdyhzdW1tYXJ5X3NlbGVjdGVkKSwgXQojICAgKQogIApgYGAKCiMgIyBDYWxjdWxhdGVzIHRvdGFsIHBsYW5rdG9uIGNvbmNlbnRyYXRpb25zIGFnZ3JlZ2F0aW5nIGRhdGEgZnJvbSBzZWxlY3RlZCBzdGF0aW9ucyAoZWcgRkspCmBgYHtyfQojIENhbGN1bGF0ZSBwbGFua3RvbiBjb25jZW50cmF0aW9uIGZvciBhbGwgc3BlY2llcyBzdW1tZWQgdXAKIyBGb3IgUGh5dG8KdGF4YV9tZXRhX2NvbmNlbnRyYXRpb25fYWxsIDwtIHRheGFfbWV0YSAlPiUKICBtdXRhdGUodG90YWxfY29uY2VudHJhdGlvbiA9ICgKICAgIENlbnRyaWMgKyBDZXJhdGl1bSArIENoYWV0b2Nlcm9zICsgQ2hhaW4yICsgQ2hhaW4zICsgR3VpbmFyZGlhICsgTmVvY2FseXB0cmVsbGEgKwogICAgTm9jdGlsdWNhICsgVHJpY2hvKSAvIHRvdGFsX3ZvbF9zYW1wbGVkICogMWU2KSAlPiUKICBzZWxlY3QoU3RhdGlvbiwgZGVjX2xhdCwgZGVjX2xvbiwgeWVhciwgbW9udGgsIGRhdGUsIEF2Zy5jaGwuYS4udWcuTC4sCiAgICAgICAgIHRlbXAuLmRlZ0MuLCBzYWxpbml0eSwgdG90YWxfdm9sX3NhbXBsZWQsIHRvdGFsX2NvbmNlbnRyYXRpb24pICU+JQogIGZpbHRlcighaXMubmEodG90YWxfdm9sX3NhbXBsZWQpKQoKIyAjIEZvciBab29wbGFua3RvbgojIHRheGFfbWV0YV9jb25jZW50cmF0aW9uX2FsbCA8LSB0YXhhX21ldGEgJT4lCiMgICBtdXRhdGUodG90YWxfY29uY2VudHJhdGlvbiA9ICgKIyAgICAgQWNhbnRoYXJlYSArIENoYWV0b2duYXRocyArIE9zdHJhY29kcyArIENvcGVwb2RzICsgRGVjYXBvZHMgKyBFY2hpbm9kZXJtcyArIEplbGxpZXMgKwojICAgICBMYXJ2YWNlYW5zICsgUG9seWNoYWV0cyArIFB0ZXJvcG9kcykgLyB0b3RhbF92b2xfc2FtcGxlZCAqIDFlNikgJT4lCiMgICBzZWxlY3QoU3RhdGlvbiwgZGVjX2xhdCwgZGVjX2xvbiwgeWVhciwgbW9udGgsIGRhdGUsIEF2Zy5jaGwuYS4udWcuTC4sCiMgICAgICAgICAgdGVtcC4uZGVnQy4sIHNhbGluaXR5LCB0b3RhbF92b2xfc2FtcGxlZCwgdG90YWxfY29uY2VudHJhdGlvbikgJT4lCiMgICBmaWx0ZXIoIWlzLm5hKHRvdGFsX3ZvbF9zYW1wbGVkKSkKCiMgRmlsdGVyIHRoZSBkYXRhIGZvciBTdGF0aW9ucwpzdGF0aW9uX2xpc3RfZmsgPC0gYygiV1MiLCIyMS9MSyIsIk1SIiwiMTYiLCIxOCIsIjEwIiwiMTIiLCI5LjUiLCI5IiwiNyIsIjIiKQpmaWx0ZXJlZF90YXhhX2NvbmNfYWxsIDwtIHRheGFfbWV0YV9jb25jZW50cmF0aW9uX2FsbCAlPiUKICBmaWx0ZXIoU3RhdGlvbiAlaW4lIHN0YXRpb25fbGlzdF9maykKCiMgQWdncmVnYXRlIHRvdGFsIGNvbmNlbnRyYXRpb24gYW5kIHRvdGFsIHZvbHVtZSBzYW1wbGVkIHBlciB5ZWFyIGFuZCBtb250aAphZ2dyZWdhdGVkX2NvbmNlbnRyYXRpb24gPC0gZmlsdGVyZWRfdGF4YV9jb25jX2FsbCAlPiUKICBncm91cF9ieSh5ZWFyLCBtb250aCkgJT4lCiAgc3VtbWFyaXNlKAogICAgdG90YWxfY291bnRzID0gc3VtKHRvdGFsX2NvbmNlbnRyYXRpb24gKiB0b3RhbF92b2xfc2FtcGxlZCAvIDFlNiwgbmEucm0gPSBUUlVFKSwgIyBTdW0gdXAgYWxsIGNvdW50cwogICAgdG90YWxfdm9sX3NhbXBsZWQgPSBzdW0odG90YWxfdm9sX3NhbXBsZWQsIG5hLnJtID0gVFJVRSksICMgU3VtIHVwIGFsbCB2b2x1bWUgc2FtcGxlZAogICAgbWVhbl90ZW1wX2RlZ0MgPSBtZWFuKHRlbXAuLmRlZ0MuLCBuYS5ybSA9IFRSVUUpLCAjIENhbGN1bGF0ZSBtZWFuIHRlbXBlcmF0dXJlCiAgICBtZWFuX3NhbGluaXR5ID0gbWVhbihzYWxpbml0eSwgbmEucm0gPSBUUlVFKSwgIyBDYWxjdWxhdGUgbWVhbiBzYWxpbml0eQogICAgbWVhbl9jaGxhID0gbWVhbihBdmcuY2hsLmEuLnVnLkwuLCBuYS5ybSA9IFRSVUUpIAogICkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZSh0b3RhbF9jb25jZW50cmF0aW9uID0gKHRvdGFsX2NvdW50cyAvIHRvdGFsX3ZvbF9zYW1wbGVkKSAqIDFlNikgIyBSZWNhbGN1bGF0ZSB0aGUgY29uY2VudHJhdGlvbgoKIyBDcmVhdGUgYSBEYXRlIGNvbHVtbiBmb3IgcGxvdHRpbmcgcHVycG9zZXMKYWdncmVnYXRlZF9jb25jZW50cmF0aW9uIDwtIGFnZ3JlZ2F0ZWRfY29uY2VudHJhdGlvbiAlPiUKICBtdXRhdGUoeWVhcl9tb250aCA9IGFzLkRhdGUocGFzdGUoeWVhciwgbW9udGgsICIwMSIsIHNlcCA9ICItIikpKQoKIyBDcmVhdGUgdGhlIHRpbWUgc2VyaWVzIHBsb3QKY29uY2VudHJhdGlvbl90cyA8LSBnZ3Bsb3QoYWdncmVnYXRlZF9jb25jZW50cmF0aW9uLCBhZXMoeCA9IHllYXJfbW9udGgpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gdG90YWxfY29uY2VudHJhdGlvbiksIGNvbG9yID0gImJsdWUiLCBzaXplID0gMSkgKyAjIExpbmUgcGxvdCBmb3IgdG90YWwgY29uY2VudHJhdGlvbgogIGdlb21fcG9pbnQoYWVzKHkgPSB0b3RhbF9jb25jZW50cmF0aW9uKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDIpICsgICMgUG9pbnRzIGZvciB0b3RhbCBjb25jZW50cmF0aW9uCiAgIyBnZW9tX2xpbmUoYWVzKHkgPSAobWVhbl90ZW1wX2RlZ0MgLSAyMCkgLyAoMzUgLSAyMCkgKiBtYXgodG90YWxfY29uY2VudHJhdGlvbikpLCAKICAjICAgICAgICAgICBjb2xvciA9ICJvcmFuZ2UiLCBzaXplID0gMSkgKyAgIyBMaW5lIHBsb3QgZm9yIHRlbXBlcmF0dXJlIChub3JtYWxpemVkKQogIGxhYnModGl0bGUgPSAiVG90YWwgUGh5dG9wbGFua3RvbiBDb25jZW50cmF0aW9uIFRpbWUgU2VyaWVzIiwKICAgICAgIHggPSAiRGF0ZSIsCiAgICAgICB5ID0gZXhwcmVzc2lvbigiVG90YWwgQ29uY2VudHJhdGlvbiAob3JnL20iXjN+IikiKSkgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcyA9ICIxIG1vbnRoIiwgZGF0ZV9sYWJlbHMgPSAiJWIgJVkiKSArICAjIFNob3cgdGlja3MgZm9yIGV2ZXJ5IG1vbnRoCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgbmFtZSA9IGV4cHJlc3Npb24oIlRvdGFsIENvbmNlbnRyYXRpb24gKG9yZy9tIl4zfiIpIiksICAgICAgICAgICAgIyBQcmltYXJ5IHktYXhpcyBsYWJlbAogICAgIyBzZWMuYXhpcyA9IHNlY19heGlzKH4gLiAqICgzNSAtIDIwKSAvIG1heChhZ2dyZWdhdGVkX2NvbmNlbnRyYXRpb24kdG90YWxfY29uY2VudHJhdGlvbikgKyAyMCwgCiAgICAjICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJUZW1wZXJhdHVyZSAowrBDKSIsIAogICAgIyAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgyMCwgMzUsIGJ5ID0gNSkpICAjIFNlY29uZGFyeSB5LWF4aXMgbGFiZWwKICApICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICMgYXhpcy50aXRsZS55LnJpZ2h0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIm9yYW5nZSIpLCAgIyBDb2xvciB0aGUgc2Vjb25kYXJ5IHktYXhpcyBsYWJlbAogICAgIyBheGlzLmxpbmUueS5yaWdodCA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJvcmFuZ2UiKSwgICAgIyBDb2xvciB0aGUgc2Vjb25kYXJ5IHktYXhpcyBsaW5lICAgCiAgICAjIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksICAjIFJlbW92ZSBtYWpvciBncmlkIGxpbmVzCiAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLCAgIyBSZW1vdmUgbWlub3IgZ3JpZCBsaW5lcwogICAgIyBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCkgICAgICAgIyBSZW1vdmUgcGFuZWwgYm9yZGVyIGlmIG5lY2Vzc2FyeQogICkKCiMgUHJpbnQgdGhlIHBsb3QKY29uY2VudHJhdGlvbl90cwpgYGAKCiMgIyBDYWxjdWxhdGUgcGxhbmt0b24gY29uY2VudHJhdGlvbnMgcGVyIHNlYXNjYXBlIGNsYXNzCmBgYHtyfQpzcHBfZGYgPC0gdGF4YV9tZXRhWyAsIGMoIlg4LmRheS5zZWFzY2FwZXMiLCAidG90YWxfdm9sX3NhbXBsZWQiLCAiZGF0ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiQWNhbnRoYXJlYSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiQ29wZXBvZHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgIkVjaGlub2Rlcm1zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJKZWxsaWVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJMYXJ2YWNlYW5zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJQb2x5Y2hhZXRzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJDaGFldG9nbmF0aHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgIlB0ZXJvcG9kcyIpXQoKIyBzcHBfZGYgPC0gdGF4YV9tZXRhWyAsIGMoIlg4LmRheS5zZWFzY2FwZXMiLCAidG90YWxfdm9sX3NhbXBsZWQiLCAiZGF0ZSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICJDZXJhdGl1bSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICJDaGFldG9jZXJvcyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICJDaGFpbjIiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhaW4zIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIkd1aW5hcmRpYSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICJOZW9jYWx5cHRyZWxsYSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICJUcmljaG8iKV0KCiMgUmVtb3ZlIHJvd3Mgd2l0aCBOYU4gdmFsdWVzIGluIFg4LmRheS5zZWFzY2FwZXMgYW5kIHRvdGFsX3ZvbF9zYW1wbGVkCnNwcF9kZiA8LSBzdWJzZXQoc3BwX2RmLCAhaXMubmEoWDguZGF5LnNlYXNjYXBlcykgJiAhaXMubmEodG90YWxfdm9sX3NhbXBsZWQpKQoKIyBDYWxjdWxhdGUgdG90YWwgY291bnRzIHBlciBzcGVjaWVzIHdpdGhpbiBlYWNoIFg4LmRheS5zZWFzY2FwZXMgY2F0ZWdvcnkKc3BwX2NvdW50X3Blcl9zZWFzY2FwZSA8LSBzcHBfZGYgJT4lCiAgZ2F0aGVyKGtleSA9ICJzcGVjaWVzIiwgdmFsdWUgPSAiY291bnQiLCAtKFg4LmRheS5zZWFzY2FwZXM6ZGF0ZSkpICU+JQogIGdyb3VwX2J5KFg4LmRheS5zZWFzY2FwZXMsIHNwZWNpZXMpICU+JQogIHN1bW1hcmlzZSh0b3RhbF9jb3VudCA9IHN1bShjb3VudCkpICU+JQogIHVuZ3JvdXAoKQoKIyBDYWxjdWxhdGUgdG90YWwgdm9sdW1lIHNhbXBsZWQgcGVyIFg4LmRheS5zZWFzY2FwZXMgY2F0ZWdvcnkKdG90YWxfdm9sX3Blcl9zZWFzY2FwZSA8LSBzcHBfZGYgJT4lCiAgZ3JvdXBfYnkoWDguZGF5LnNlYXNjYXBlcykgJT4lCiAgc3VtbWFyaXNlKHRvdGFsX3ZvbF9zYW1wbGVkID0gc3VtKHRvdGFsX3ZvbF9zYW1wbGVkKSkKCiMgTWVyZ2UgdG90YWwgY291bnRzIGFuZCB0b3RhbCB2b2x1bWUgZGF0YSBmcmFtZXMKc3BwX2NvbmNlbnRyYXRpb24gPC0gbWVyZ2Uoc3BwX2NvdW50X3Blcl9zZWFzY2FwZSwgdG90YWxfdm9sX3Blcl9zZWFzY2FwZSwgYnkgPSAiWDguZGF5LnNlYXNjYXBlcyIpCgojIENhbGN1bGF0ZSBzcGVjaWVzIHBlciBjdWJpYyBtZXRlcjogbWlnaHQgYmUgbWlzbGVhZGluZyBzaW5jZSBpdCBpbnRlZ3JhdGVzIGFsbCBjb3VudHMgYW5kIHNhbXBsZWQgdm9sdW1lcyBhY3Jvc3MgY3J1aXNlcyB0byBjYWxjdWxhdGUgY29uY2VudHJhdGlvbnMuIEl0IGlzIGxpa2VseSBiZXR0ZXIgdG8gdXNlIGF2ZXJhZ2UgY29uY2VudHJhdGlvbnMgYXMgYmVsb3cuCnNwcF9jb25jZW50cmF0aW9uJHNwZWNpZXNfcGVyX2N1YmljX21ldGVyIDwtIHNwcF9jb25jZW50cmF0aW9uJHRvdGFsX2NvdW50IC8gc3BwX2NvbmNlbnRyYXRpb24kdG90YWxfdm9sX3NhbXBsZWQgKiAxZTYKCiMgIyBTZWxlY3QgZGVzaXJlZCBzZWFzY2FwZXMgYW5kIHJlb3JkZXIgY2F0ZWdvcmllcyBpbiBYIGF4aXMKc3BwX2NvbmNlbnRyYXRpb24kWDguZGF5LnNlYXNjYXBlcyA8LSBmYWN0b3Ioc3BwX2NvbmNlbnRyYXRpb24kWDguZGF5LnNlYXNjYXBlcywgbGV2ZWxzID0gYygiMyIsICI1IiwgIjciLCAiMTEiLCAiMTMiLCAiMTUiLCIyMSIsIjI3IikpCgojIEZpbHRlciBvdXQgc2Vhc2NhcGUgY2xhc3MgYXMgZGVzaXJlZAojIGV4Y2x1ZGVfY2xhc3NlcyA8LSBjKDUsIDcsIDExKQojIHNwcF9jb25jZW50cmF0aW9uIDwtIHNwcF9jb25jZW50cmF0aW9uWyFzcHBfY29uY2VudHJhdGlvbiRYOC5kYXkuc2Vhc2NhcGVzICVpbiUgZXhjbHVkZV9jbGFzc2VzLCBdCgpjdXN0b21fcGFsX2hleDIgPC0gYygnI2U0MWExYycsJyMzNzdlYjgnLCcjNGRhZjRhJywnIzk4NGVhMycsJyNmZjdmMDAnLCcjZmZmZjMzJywnI2E2NTYyOCcsJyNmNzgxYmYnKQoKIyBDcmVhdGUgdGhlIHN0YWNrIHBsb3QKY29uY2VudHJhdGlvbl9zdGFja3Bsb3QgPC0gZ2dwbG90KHNwcF9jb25jZW50cmF0aW9uLCBhZXMoeCA9IFg4LmRheS5zZWFzY2FwZXMsIHkgPSBzcGVjaWVzX3Blcl9jdWJpY19tZXRlciwgZmlsbCA9IHNwZWNpZXMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcGFsX2hleDIpICsKICBsYWJzKHggPSAiU2Vhc2NhcGUgY2xhc3MiLCB5ID0gIlNwZWNpZXMgY29uY2VudHJhdGlvbiAob3JnLiBtIl4iLTMifiIpIikgKwp0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksICAjIFNldCBYLWF4aXMgbGFiZWwgZm9udCBzaXplCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSkKY29uY2VudHJhdGlvbl9zdGFja3Bsb3QKCiMgQ2FsY3VsYXRlIHBlcmNlbnRhZ2VzCnNwcF9jb25jZW50cmF0aW9uX3BlcmNlbnQgPC0gc3BwX2NvbmNlbnRyYXRpb24gJT4lCiAgZ3JvdXBfYnkoWDguZGF5LnNlYXNjYXBlcykgJT4lCiAgbXV0YXRlKHRvdGFsX2NvbmNlbnRyYXRpb24gPSBzdW0oc3BlY2llc19wZXJfY3ViaWNfbWV0ZXIpLAogICAgICAgICBzcGVjaWVzX3BlcmNlbnRhZ2UgPSAoc3BlY2llc19wZXJfY3ViaWNfbWV0ZXIgLyB0b3RhbF9jb25jZW50cmF0aW9uKSAqIDEwMCkKCiMgQ3JlYXRlIHRoZSBzdGFja2VkIHBsb3QKY29uY2VudHJhdGlvbl9wZXJjZW50X3N0YWNrcGxvdCA8LSBnZ3Bsb3Qoc3BwX2NvbmNlbnRyYXRpb25fcGVyY2VudCwgYWVzKHggPSBYOC5kYXkuc2Vhc2NhcGVzLCB5ID0gc3BlY2llc19wZXJjZW50YWdlLCBmaWxsID0gc3BlY2llcykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9wYWxfaGV4MikgKwogIGxhYnMoeCA9ICJTZWFzY2FwZSBjbGFzcyIsIHkgPSAiUmVsYXRpdmUgY29uY2VudHJhdGlvbiAoJSkiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLCAgIyBTZXQgWC1heGlzIGxhYmVsIGZvbnQgc2l6ZQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpCmNvbmNlbnRyYXRpb25fcGVyY2VudF9zdGFja3Bsb3QKCiMgR2F0aGVyIGNvbHVtbnMgY29udGFpbmluZyBzcGVjaWVzIGNvdW50cyBpbnRvIGtleS12YWx1ZSBwYWlycyBhbmQgY2FsY3VsYXRlIGNvbmNlbnRyYXRpb25zCnNwcF9jb25jZW50cmF0aW9uX2xvbmcgPC0gc3BwX2RmICU+JQogIGdhdGhlcihrZXkgPSAic3BlY2llcyIsIHZhbHVlID0gImNvdW50IiwgLShYOC5kYXkuc2Vhc2NhcGVzOmRhdGUpKSAlPiUKICBtdXRhdGUoY29uY2VudHJhdGlvbiA9IChjb3VudCAvIHRvdGFsX3ZvbF9zYW1wbGVkKSAqIDFlNikKCiMgQ2FsY3VsYXRlIHRoZSBtZWFuIGNvbmNlbnRyYXRpb24gYW5kIGl0cyBzdGFuZGFyZCBkZXZpYXRpb24gcGVyIHNwZWNpZXMgYW5kIDhYLmRheS5zZWFzY2FwZXMgY2F0ZWdvcnkuIFRoaXMgaXMgbGlrZWx5IG1vcmUgcmVwcmVzZW50YXRpdmUgc2luY2UgdGhlIGFwcHJvYWNoIHdpbGwgY2FwdHVyZSBoaWdoIGNvbmNlbnRyYXRpb24gZXZlbnRzIChoaWdoIGNvdW50cyBpbiBsb3cgdm9sdW1lcykgdGhhdCBvdGhlcndpc2UgZ2V0IGZpbHRlcmVkIG91dCB3aGVuIHVzaW5nIGFuIG92ZXJhbGwgaW50ZWdyYXRlZCBzYW1wbGVkIHZvbHVtZSBhbmQgdG90YWwgY291bnRzIGZvciB0aGUgdG90YWwgY29uY2VudHJhdGlvbi4KYXZnX2NvbmNlbnRyYXRpb25fcGVyX2NsYXNzIDwtIHNwcF9jb25jZW50cmF0aW9uX2xvbmcgJT4lCiAgZ3JvdXBfYnkoc3BlY2llcywgYFg4LmRheS5zZWFzY2FwZXNgKSAlPiUKICBzdW1tYXJpemUoCiAgICBtZWFuX2NvbmNlbnRyYXRpb24gPSBtZWFuKGNvbmNlbnRyYXRpb24sIG5hLnJtID0gVFJVRSksCiAgICBzZF9jb25jZW50cmF0aW9uID0gc2QoY29uY2VudHJhdGlvbiwgbmEucm0gPSBUUlVFKQogICkKCmF2Z19jb25jZW50cmF0aW9uX3Blcl9jbGFzcyRYOC5kYXkuc2Vhc2NhcGVzIDwtIGZhY3RvcihhdmdfY29uY2VudHJhdGlvbl9wZXJfY2xhc3MkWDguZGF5LnNlYXNjYXBlcywgbGV2ZWxzID0gYygiMyIsICI1IiwgIjciLCAiMTEiLCAiMTMiLCAiMTUiLCIyMSIsIjI3IikpCgphdmdfc3RhY2twbG90IDwtIGdncGxvdChhdmdfY29uY2VudHJhdGlvbl9wZXJfY2xhc3MsIGFlcyh4ID0gWDguZGF5LnNlYXNjYXBlcywgeSA9IG1lYW5fY29uY2VudHJhdGlvbiwgZmlsbCA9IHNwZWNpZXMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICAjIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9wYWxfaGV4MikgKyAjIHVzZSBmb3Igem9vcGxhbmt0b24KICBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MSIpICsgIyB1c2UgZm9yIHBoeXRvcGxhbmt0b24KICBsYWJzKHggPSAiU2Vhc2NhcGUgY2xhc3MiLCB5ID0gIk1lYW4gZGVuc2l0eSAob3JnLiBtIl4iLTMifiIpIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpKQphdmdfc3RhY2twbG90CgojIENhbGN1bGF0ZSBwZXJjZW50YWdlcwphdmdfY29uY2VudHJhdGlvbl9wZXJjZW50IDwtIGF2Z19jb25jZW50cmF0aW9uX3Blcl9jbGFzcyAlPiUKICBncm91cF9ieShgWDguZGF5LnNlYXNjYXBlc2ApICU+JQogIG11dGF0ZShtZWFuX2NvbmNlbnRyYXRpb25fcGVyY2VudCA9IG1lYW5fY29uY2VudHJhdGlvbiAvIHN1bShtZWFuX2NvbmNlbnRyYXRpb24pICogMTAwKQoKYXZnX3BlcmNlbnRfc3RhY2twbG90IDwtIGdncGxvdChhdmdfY29uY2VudHJhdGlvbl9wZXJjZW50LCBhZXMoeCA9IFg4LmRheS5zZWFzY2FwZXMsIHkgPSBtZWFuX2NvbmNlbnRyYXRpb25fcGVyY2VudCwgZmlsbCA9IHNwZWNpZXMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fcGFsX2hleDIpICsgIyB1c2UgZm9yIHpvb3BsYW5rdG9uCiAgIyBzY2FsZV9jb2xvdXJfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsgIyB1c2UgZm9yIHBoeXRvcGxhbmt0b24KICBsYWJzKHggPSAiU2Vhc2NhcGUgY2xhc3MiLCB5ID0gIlJlbGF0aXZlIGNvbmNlbnRyYXRpb24gKCUpIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpKQphdmdfcGVyY2VudF9zdGFja3Bsb3QKCmBgYAoKCiMgR2VuZXJhdGUgcGxvdHMgCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShocmJydGhlbWVzKQpsaWJyYXJ5KHZpcmlkaXMpCgojICMgTG9hZCBzZWFzY2FwZSBjb2xvciBwYWxldHRlIHVzZWQgd2l0aCBNYXRsYWIgYW5kIGV4dHJhY3QgUkdCIHZhbHVlcyBmb3Igb2JzZXJ2ZWQgdW5pcXVlIHNlYXNjYXBlcwojIEZvciBOT0FBIG1hY2hpbmVzCiMgcGFsZXR0ZV9kaXIgPC0gIi9Vc2Vycy9lbnJpcXVlLm1vbnRlcy9MaWJyYXJ5L0Nsb3VkU3RvcmFnZS9Hb29nbGVEcml2ZS1lbnJpcXVlbW9udGVzMDFAZ21haWwuY29tL015IERyaXZlL0dEcml2ZS9zb2Z0d2FyZS9tYXRsYWIvbV9tYXAvc2Vhc2NhcGVfY20iCiMgRm9yIHBlcnNvbmFsIG1hY2hpbmUKcGFsZXR0ZV9kaXIgPC0gIn4vTGlicmFyeS9DbG91ZFN0b3JhZ2UvR29vZ2xlRHJpdmUtZW5yaXF1ZW1vbnRlczAxQGdtYWlsLmNvbS9NeSBEcml2ZS9HRHJpdmUvc29mdHdhcmUvbWF0bGFiL21fbWFwL3NlYXNjYXBlX2NtIgpwYWxldHRlX2ZpbGUgPC0gbGlzdC5maWxlcyhwYXRoID0gcGFsZXR0ZV9kaXIsIHBhdHRlcm4gPSAiY21hcDEuY3N2IiwgZnVsbC5uYW1lcyA9IFRSVUUpCnBhbGV0dGVfZGYgPC0gcmVhZC5jc3YocGFsZXR0ZV9maWxlLCBoZWFkZXIgPSBGQUxTRSkKY29sbmFtZXMocGFsZXR0ZV9kZikgPC0gYygiciIsICJnIiwgImIiKQp1bmlxdWVfc2Vhc2NhcGVzIDwtIHNvcnQodW5pcXVlKG5hLm9taXQoY3RkX21ldGEkWDguZGF5LnNlYXNjYXBlcykpKQpzdWJzZXRfcGFsZXR0ZV9kZiA8LSBwYWxldHRlX2RmW3VuaXF1ZV9zZWFzY2FwZXMsIF0KCiMgc2V0IFJHQiB2YWx1ZXMgZm9yIHRoZSBwbG90cwpyX3ZhbHMgPC0gcm91bmQoc3Vic2V0X3BhbGV0dGVfZGYkciAqIDI1NSwgMCkKZ192YWxzIDwtIHJvdW5kKHN1YnNldF9wYWxldHRlX2RmJGcgKiAyNTUsIDApCmJfdmFscyA8LSByb3VuZChzdWJzZXRfcGFsZXR0ZV9kZiRiICogMjU1LCAwKQpjdXN0b21fcGFsIDwtIGNiaW5kKHJfdmFscywgZ192YWxzLCBiX3ZhbHMpCmN1c3RvbV9wYWxfaGV4IDwtIHJnYihjdXN0b21fcGFsWywgMV0sIGN1c3RvbV9wYWxbLCAyXSwgY3VzdG9tX3BhbFssIDNdLCBtYXhDb2xvclZhbHVlPTI1NSkKIyBwYWxfZmluYWwgPC0gYyhjdXN0b21fcGFsX2hleFszXSwgY3VzdG9tX3BhbF9oZXhbNF0sIGN1c3RvbV9wYWxfaGV4WzVdLCApCgojIGZpbHRlciBvdXQgcm93cyB3aXRoIE5BIHZhbHVlcyBpbiBjb2x1bW4gc2Vhc2NhcGVzCmRmX2ZpbHRlcmVkIDwtIHRheGFfbWV0YVtjb21wbGV0ZS5jYXNlcyh0YXhhX21ldGEkWDguZGF5LnNlYXNjYXBlcyksXQoKIyBDb252ZXJ0IHRoZSAneCcgY29sdW1uIHRvIGNoYXJhY3RlcgpkZl9maWx0ZXJlZCRYOC5kYXkuc2Vhc2NhcGVzIDwtIGFzLmNoYXJhY3RlcihkZl9maWx0ZXJlZCRYOC5kYXkuc2Vhc2NhcGVzKQoKIyAjIFJlb3JkZXIgc2Vhc2NhcGUgY2F0ZWdvcmllcyBpbiBYIGF4aXMKZGZfZmlsdGVyZWQkWDguZGF5LnNlYXNjYXBlcyA8LSBmYWN0b3IoZGZfZmlsdGVyZWQkWDguZGF5LnNlYXNjYXBlcywgbGV2ZWxzID0gYygiMyIsICI1IiwgIjciLCAiMTEiLCAiMTMiLCAiMTUiLCIyMSIsIjI3IikpCgojIERlZmluZSBjdXN0b20gY29sb3JzIGZvciBlYWNoIGxldmVsCmN1c3RvbV9jb2xvcnMgPC0gYygiMyIgPSBjdXN0b21fcGFsX2hleFsxXSwgIjUiID0gY3VzdG9tX3BhbF9oZXhbMl0sICI3IiA9IGN1c3RvbV9wYWxfaGV4WzNdLAogICAgICAgICAgICAgICAgICAgIjExIiA9IGN1c3RvbV9wYWxfaGV4WzRdLCAiMTMiID0gY3VzdG9tX3BhbF9oZXhbNV0sICIxNSIgPSBjdXN0b21fcGFsX2hleFs2XSwKICAgICAgICAgICAgICAgICAgICIyMSIgPSBjdXN0b21fcGFsX2hleFs3XSwgIjI3IiA9IGN1c3RvbV9wYWxfaGV4WzhdKQoKIyBGaWx0ZXIgb3V0IHNlYXNjYXBlIGNsYXNzIGFzIGRlc2lyZWQKIyBkZl9maWx0ZXJlZCA8LSBkZl9maWx0ZXJlZFtkZl9maWx0ZXJlZCRYOC5kYXkuc2Vhc2NhcGVzICE9ICI1IiwgXQoKIyBQbG90CiMgU2VlIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy92aXJpZGlzL3ZpZ25ldHRlcy9pbnRyby10by12aXJpZGlzLmh0bWwgZm9yIGNvbG9yIHBhbGV0dGUgb3B0aW9ucwpwcCA8LSBkZl9maWx0ZXJlZCAlPiUKICBnZ3Bsb3QoIGFlcyh4PVg4LmRheS5zZWFzY2FwZXMsIHk9QXZnLmNobC5hLi51Zy5MLiwgZmlsbD1YOC5kYXkuc2Vhc2NhcGVzKSkgKwogICAgZ2VvbV9ib3hwbG90KCkgKwogICAgIyBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uPSJIIiwgZGlzY3JldGUgPSBUUlVFLCBhbHBoYT0wLjYpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnMpICsKICAgIGdlb21faml0dGVyKGNvbG9yPSJncmV5Iiwgc2l6ZT0wLjgsIGFscGhhPTAuNCkgKwogICAgbGFicyh4ID0gIlNlYXNjYXBlIGNsYXNzIikgKwogICAgbGFicyh5ID0gZXhwcmVzc2lvbigiWyIqQ2hsLWEqIl0ifiBtdSoiZyJ+TF4tMSkpICsKICAgICMgbGFicyh5ID0gZXhwcmVzc2lvbigiU2FsaW5pdHkiKSkgKwogICAgIyBsYWJzKHkgPSBleHByZXNzaW9uKHBhc3RlKCJUZW1wZXJhdHVyZSAoIiwgZGVncmVlLCAiQykgYXQgMSBtIGRlcHRoIikpKSArCiAgICAjIGxhYnMoeSA9IGV4cHJlc3Npb24oIlsiKkRPKiJdIn5tZ35MXi0xKSkgKwogICAgIyBsYWJzKHkgPSBleHByZXNzaW9uKCJbIipOT1sieCJdKiJdIiB+IG11KiJNIikpICsKICAgICMgbGFicyh5ID0gZXhwcmVzc2lvbigiWyIqUE9bNF1eIjMtIioiXSIgfiBtdSoiTSIpKSArCiAgICAjIGxhYnMoeSA9IGV4cHJlc3Npb24oIlsiKk5IWzRdXiIrIioiXSIgfiBtdSoiTSIpKSArCiAgICAjIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpKQogICAgIyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz0gYygiVHJvcGljYWwvU3VidHJvcGljYWwgVXB3ZWxsaW5nIiwgCiAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJvcGljYWwgU2VhcyIsIAogICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgIldhcm0sIEJsb29tcywgSGlnaCBOdXRzIiwgCiAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJvcGljYWwvU3VidHJvcGljYWwgVHJhbnNpdGlvbiIsIAogICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgIlRlbXBlcmF0ZSBUcmFuc2l0aW9uIikpICsKICAgIHRoZW1lX2lwc3VtKCkgKwogICAgdGhlbWUoCiAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSkKICAgICkgKwogICAgIyBnZ3RpdGxlKCJBIGJveHBsb3Qgd2l0aCBqaXR0ZXIiKSArCiAgICAjIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUpKSArCiAgeWxpbSgwLCA3KSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyLCBmYW1pbHkgPSAiQXJpYWwiKSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMiwgZmFtaWx5ID0gIkFyaWFsIikpICsKICAgIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGhqdXN0ID0gMC41LCBmYW1pbHkgPSAiQXJpYWwiKSwKICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGhqdXN0ID0gMC41LCBmYW1pbHkgPSAiQXJpYWwiKSkgKwogICAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSkgCnBwCmBgYAoKIyBDcmVhdGUgc3RhY2twbG90cyBzaG93aW5nIHJlbGF0aXZlIGZyZXF1ZW5jeSB1c2luZyBjb3VudHMgb25seSBvZiBwbGFua3RvbiB0YXhhIHBlciBzZWFzY2FwZSBjYXRlZ29yeQpgYGB7cn0KIyBjb252ZXJ0IGFidW5kYW5jZSBjb2x1bW5zIHRvIHJlbGF0aXZlIGZyZXF1ZW5jeQoKIyBzdWJzZXRzIHRheGEgZm9yIHpvb3BsYW5rdG9uCiMgZGZfc3Vic2V0IDwtIGRmX2ZpbHRlcmVkWyAsIGMoIlg4LmRheS5zZWFzY2FwZXMiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBY2FudGhhcmVhIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29wZXBvZHMiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFY2hpbm9kZXJtcyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkplbGxpZXMiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJMYXJ2YWNlYW5zIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUG9seWNoYWV0ZXMiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDaGFldG9nbmF0aHMiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQdGVyb3BvZHMiKV0KCiMgc3Vic2V0cyB0YXhhIGZvciBwaHl0b3BsYW5rdG9uCmRmX3N1YnNldCA8LSBkZl9maWx0ZXJlZFsgLCBjKCJYOC5kYXkuc2Vhc2NhcGVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDZXJhdGl1bSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhZXRvY2Vyb3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYWluMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhaW4zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHdWluYXJkaWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5lb2NhbHlwdHJlbGxhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUcmljaG8iKV0KCiMgc3Vic2V0cyB0YXhhIGZvciBhbGwgc3BlY2llcwojIGRmX3N1YnNldCA8LSBkZl9maWx0ZXJlZFsgLCBjKCJYOC5kYXkuc2Vhc2NhcGVzIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNlcmF0aXVtIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYWV0b2Nlcm9zIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkRpYXRvbXMiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRGlhdG9tczIiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3VpbmFyZGlhIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5lb2NhbHlwdHJlbGxhIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRyaWNob2Rlc21pdW0iLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQWNhbnRoYXJlYSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDb3BlcG9kcyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJFY2hpbm9kZXJtcyIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJKZWxsaWVzIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkxhcnZhY2VhbnMiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUG9seWNoYWV0ZXMiLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhZXRvZ25hdGhzIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlB0ZXJvcG9kcyIpXQoKIyByZXNoYXBlIHRoZSBkYXRhIHRvIGxvbmcgZm9ybWF0IApkZl9sb25nIDwtIHRpZHlyOjpnYXRoZXIoZGZfc3Vic2V0LCBrZXkgPSAiU3BlY2llcyIsIHZhbHVlID0gIkZyZXF1ZW5jeSIsIC1YOC5kYXkuc2Vhc2NhcGVzKQoKIyBjYWxjdWxhdGUgcmVsYXRpdmUgZnJlcXVlbmNpZXMKZGZfc3VtIDwtIGRmX2xvbmcgJT4lCiAgZ3JvdXBfYnkoWDguZGF5LnNlYXNjYXBlcywgU3BlY2llcykgJT4lCiAgc3VtbWFyaXNlKG4gPSBzdW0oRnJlcXVlbmN5KSkgJT4lCiAgbXV0YXRlKGZyZXEgPSBuIC8gc3VtKG4pKQoKIyBTZWFzY2FwZSBjbGFzcyBuYW1lczoKIyAiQ2xhc3MgMTEiIC0gVHJvcGljYWwvU3VidHJvcGljYWwgVXB3ZWxsaW5nCiMgIkNsYXNzIDE1IiAtIFRyb3BpY2FsIFNlYXMKIyAiQ2xhc3MgMjEiIC0gV2FybSwgQmxvb21zLCBIaWdoIE51dHMKIyAiQ2xhc3MgMyIgLSBUcm9waWNhbC9TdWJ0cm9waWNhbCBUcmFuc2l0aW9uCiMgIkNsYXNzIDciIC0gVGVtcGVyYXRlIFRyYW5zaXRpb24KCiMgIyBTZWxlY3QgZGVzaXJlZCBzZWFzY2FwZXMgYW5kIHJlb3JkZXIgY2F0ZWdvcmllcyBpbiBYIGF4aXMKZGZfc3VtJFg4LmRheS5zZWFzY2FwZXMgPC0gZmFjdG9yKGRmX3N1bSRYOC5kYXkuc2Vhc2NhcGVzLCBsZXZlbHMgPSBjKCIzIiwgIjUiLCAiNyIsICIxMSIsICIxMyIsICIxNSIsIjIxIiwiMjciKSkKCiMgRmlsdGVyIG91dCBzZWFzY2FwZSBjbGFzcyBhcyBkZXNpcmVkCmV4Y2x1ZGVfY2xhc3NlcyA8LSBjKDUsIDcsIDExKQpkZl9zdW0gPC0gZGZfc3VtWyFkZl9zdW0kWDguZGF5LnNlYXNjYXBlcyAlaW4lIGV4Y2x1ZGVfY2xhc3NlcywgXQoKIyBVc2UgcXVhbGl0YXRpdmUgcGFsZXR0ZXM6IGh0dHBzOi8vY29sb3JicmV3ZXIyLm9yZy8jdHlwZT1xdWFsaXRhdGl2ZSZzY2hlbWU9QWNjZW50Jm49NwpjdXN0b21fcGFsX2hleDIgPC0gYygnI2U0MWExYycsJyMzNzdlYjgnLCcjNGRhZjRhJywnIzk4NGVhMycsJyNmZjdmMDAnLCcjZmZmZjMzJywnI2E2NTYyOCcsJyNmNzgxYmYnKQojIGN1c3RvbV9wYWxfaGV4MiA8LSBjKCcjMWI5ZTc3JywnI2Q5NWYwMicsJyM3NTcwYjMnLCcjZTcyOThhJywnIzY2YTYxZScsJyNlNmFiMDInLCcjYTY3NjFkJykKCiMgY3JlYXRlIHRoZSBzdGFja3Bsb3QKcXEgPC0gZ2dwbG90KGRmX3N1bSwgYWVzKHggPSBYOC5kYXkuc2Vhc2NhcGVzLCB5ID0gZnJlcSwgZmlsbCA9IFNwZWNpZXMpKSArCiAgIyBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uPSJTZXQzIiwgZGlzY3JldGUgPSBUUlVFLCBhbHBoYT0wLjgpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGUgPSBUUlVFLCBhbHBoYT0wLjgpICsKICAjIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9wYWxfaGV4MikgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgbGFicyh4ID0gIlNlYXNjYXBlIGNsYXNzIikgKwogIGxhYnMoeSA9ICJSZWxhdGl2ZSBmcmVxdWVuY3kiKSArCiAgIyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz0gYygiVHJvcGljYWwvU3VidHJvcGljYWwgVXB3ZWxsaW5nIiwgCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJvcGljYWwgU2VhcyIsIAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldhcm0sIEJsb29tcywgSGlnaCBOdXRzIiwgCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJvcGljYWwvU3VidHJvcGljYWwgVHJhbnNpdGlvbiIsIAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRlbXBlcmF0ZSBUcmFuc2l0aW9uIikpICsKICAjIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpLCAgIyBTZXQgWC1heGlzIGxhYmVsIGZvbnQgc2l6ZQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpIAogICN0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDEpKQpxcQpgYGAKCgojIENvbXB1dGUgU2hhbm5vbiBJbmRleApgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeSh2ZWdhbikKCiMgU2VsZWN0IHNwZWNpZXMgZm9yIFNoYW5ub24gY2FsY3VsYXRpb24KZGZfc3Vic2V0X3NoYW5ub24gPC0gZGZfZmlsdGVyZWRbICwgYygiWDguZGF5LnNlYXNjYXBlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBY2FudGhhcmVhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvcGVwb2RzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkVjaGlub2Rlcm1zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkplbGxpZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGFydmFjZWFucyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQb2x5Y2hhZXRzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYWV0b2duYXRocyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQdGVyb3BvZHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2VyYXRpdW0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhZXRvY2Vyb3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhaW4yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNoYWluMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHdWluYXJkaWEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTmVvY2FseXB0cmVsbGEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJpY2hvIildCgojIHJlc2hhcGUgdGhlIGRhdGEgdG8gbG9uZyBmb3JtYXQKZGZfbG9uZyA8LSB0aWR5cjo6Z2F0aGVyKGRmX3N1YnNldF9zaGFubm9uLCBrZXkgPSAiU3BlY2llcyIsIHZhbHVlID0gIkFidW5kYW5jZSIsIC1YOC5kYXkuc2Vhc2NhcGVzKQoKIyBFeGNsdWRlIHNlYXNjYXBlcwpleGNsdWRlX3NlYXNjYXBlcyA8LSBjKDUsIDcsIDExKQoKIyBDb21wdXRlIFNoYW5ub24gZGl2ZXJzaXR5IHBlciBzZWFzY2FwZSBjbGFzcwpzaGFubm9uX2RmIDwtIGRmX2xvbmcgJT4lIAogIGdyb3VwX2J5KFg4LmRheS5zZWFzY2FwZXMpICU+JSAKICBzdW1tYXJpc2Uoc2hhbm5vbiA9IGRpdmVyc2l0eShBYnVuZGFuY2UsIGluZGV4ID0gInNoYW5ub24iKSkKCiMgRmlsdGVyIHRoZSBkYXRhIHRvIGV4Y2x1ZGUgc3BlY2lmaWVkIHNlYXNjYXBlcwpzaGFubm9uX2RmX2ZpbHRlcmVkIDwtIHNoYW5ub25fZGZbIXNoYW5ub25fZGYkWDguZGF5LnNlYXNjYXBlcyAlaW4lIGV4Y2x1ZGVfc2Vhc2NhcGVzLCBdCgojIGNyZWF0ZSB0aGUgYmFyIHBsb3Qgb2YgU2hhbm5vbiBkaXZlcnNpdHkKZmYgPC0gZ2dwbG90KHNoYW5ub25fZGZfZmlsdGVyZWQsIGFlcyh4ID0gWDguZGF5LnNlYXNjYXBlcywgeSA9IHNoYW5ub24sIGZpbGwgPSBYOC5kYXkuc2Vhc2NhcGVzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgIyBzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uPSJwbGFzbWEiLCBkaXNjcmV0ZSA9IFRSVUUsIGFscGhhPTAuNikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnMpICsKICB4bGFiKCJTZWFzY2FwZSBDbGFzcyIpICsKICB5bGFiKCJTaGFubm9uIERpdmVyc2l0eSIpICsKICAjIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPSBjKCJUcm9waWNhbC9TdWJ0cm9waWNhbCBVcHdlbGxpbmciLCAKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUcm9waWNhbCBTZWFzIiwgCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAiV2FybSwgQmxvb21zLCBIaWdoIE51dHMiLCAKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUcm9waWNhbC9TdWJ0cm9waWNhbCBUcmFuc2l0aW9uIiwgCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVGVtcGVyYXRlIFRyYW5zaXRpb24iKSkgKwogICMgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSkpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpLCAgIyBTZXQgWC1heGlzIGxhYmVsIGZvbnQgc2l6ZQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSkgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpICsKICBndWlkZXMoKQpmZgoKIyAjIFN1YnNldCB0aGUgZGF0YSB0byBleGNsdWRlIHNwZWNpZmllZCBzZWFzY2FwZXMKZmlsdGVyZWRfdGF4YV9tZXRhIDwtIHRheGFfbWV0YVtjb21wbGV0ZS5jYXNlcyh0YXhhX21ldGEkWDguZGF5LnNlYXNjYXBlcyksIF0KZmlsdGVyZWRfdGF4YV9tZXRhIDwtIGZpbHRlcmVkX3RheGFfbWV0YVshZmlsdGVyZWRfdGF4YV9tZXRhJFg4LmRheS5zZWFzY2FwZXMgJWluJSBleGNsdWRlX3NlYXNjYXBlcywgXQoKIyAjIFBsb3Qgc2FtcGxlZCBzZWFzY2FwZSBmcmVxdWVuY3kKdHQgPC0gZ2dwbG90KGZpbHRlcmVkX3RheGFfbWV0YSwgYWVzKHggPSBmYWN0b3IoWDguZGF5LnNlYXNjYXBlcyksIGZpbGwgPSBmYWN0b3IoWDguZGF5LnNlYXNjYXBlcykpKSArCiAgZ2VvbV9iYXIoY29sb3IgPSAiYmxhY2siLCBhbHBoYSA9IDAuNykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnMpICsKICBsYWJzKHggPSAiU2Vhc2NhcGUgQ2xhc3MiLCB5ID0gIkZyZXF1ZW5jeSIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIyKSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjIpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMikpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjIpKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkKdHQKCiMgIyBDb3VudHMgb2Ygc3BlY2llcyBwZXIgc2Vhc2NhcGUKZGZfc3VtX2FidW5kIDwtIGRmX2xvbmcgJT4lCiAgZ3JvdXBfYnkoWDguZGF5LnNlYXNjYXBlcywgU3BlY2llcykgJT4lCiAgc3VtbWFyaXNlKG4gPSBzdW0oQWJ1bmRhbmNlKSkgCgpkZl9zdW1fYWJ1bmRfZmlsdCA8LSBkZl9zdW1fYWJ1bmRbZGZfc3VtX2FidW5kJFNwZWNpZXMgPT0gIlB0ZXJvcG9kcyIsXQojIEZpbHRlciB0aGUgZGF0YSB0byBleGNsdWRlIHNwZWNpZmllZCBzZWFzY2FwZXMKZGZfc3VtX2FidW5kX2ZpbHQyIDwtIGRmX3N1bV9hYnVuZF9maWx0WyFkZl9zdW1fYWJ1bmRfZmlsdCRYOC5kYXkuc2Vhc2NhcGVzICVpbiUgZXhjbHVkZV9zZWFzY2FwZXMsIF0KCmRkIDwtIGdncGxvdChkZl9zdW1fYWJ1bmRfZmlsdDIsIGFlcyh4ID0gZmFjdG9yKFg4LmRheS5zZWFzY2FwZXMpLCB5ID0gbiwgZmlsbCA9IGZhY3RvcihYOC5kYXkuc2Vhc2NhcGVzKSkpICsKICBnZW9tX2Jhcihjb2xvciA9ICJibGFjayIsIGFscGhhID0gMC43LCBzdGF0ID0gImlkZW50aXR5IikgKwogICMgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbiA9ICJtYWdtYSIsIGRpc2NyZXRlID0gVFJVRSwgYWxwaGEgPSAwLjgpCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tX2NvbG9ycykgKwogIHRoZW1lX21pbmltYWwoKSAKZGQKCiMgQ29tcHV0ZSBjb3VudHMgb2Ygc2VsZWN0ZWQgc3BlY2llcyBwZXIgc2Vhc2NhcGUgY2xhc3Mgbm9ybWFsaXplZCBieSBmcmVxdWVuY3kgb2Ygc2Vhc2NhcGVzCiMgcmVzaGFwZSB0aGUgZGF0YSB0byBsb25nIGZvcm1hdApkZl9sb25nMiA8LSB0aWR5cjo6Z2F0aGVyKGRmX3N1YnNldF9zaGFubm9uLCBrZXkgPSAiU3BlY2llcyIsIHZhbHVlID0gIkFidW5kYW5jZSIsIC1YOC5kYXkuc2Vhc2NhcGVzKQoKc3BwX3NlYXNjYXBlX25vcm1hbGl6ZWQgPC0gZGZfbG9uZzIgJT4lCiAgZmlsdGVyKFNwZWNpZXMgPT0gIlB0ZXJvcG9kcyIpICU+JQogIGdyb3VwX2J5KFg4LmRheS5zZWFzY2FwZXMpICU+JQogIHN1bW1hcmlzZShzcHBfY291bnQgPSBzdW0oQWJ1bmRhbmNlKSkKCiMgQ29tcHV0ZSBmcmVxdWVuY2llcyBvZiBlYWNoIHNlYXNjYXBlIGNsYXNzCnNlYXNjYXBlX2ZyZXF1ZW5jaWVzIDwtIGRmX2xvbmcyICU+JQogIGNvdW50KFg4LmRheS5zZWFzY2FwZXMpICU+JQogIHJlbmFtZShmcmVxID0gbikKCiMgTWVyZ2UgY291bnRzIHdpdGggZnJlcXVlbmNpZXMKc3BwX3NlYXNjYXBlX25vcm1hbGl6ZWQgPC0gbWVyZ2Uoc3BwX3NlYXNjYXBlX25vcm1hbGl6ZWQsIHNlYXNjYXBlX2ZyZXF1ZW5jaWVzLCBieSA9ICJYOC5kYXkuc2Vhc2NhcGVzIikKCiMgTm9ybWFsaXplIGNvdW50cyBieSBmcmVxdWVuY3kKc3BwX3NlYXNjYXBlX25vcm1hbGl6ZWQkc3BwX25vcm1hbGl6ZWQgPC0gKHNwcF9zZWFzY2FwZV9ub3JtYWxpemVkJHNwcF9jb3VudCAvIHNwcF9zZWFzY2FwZV9ub3JtYWxpemVkJGZyZXEpICogMTAwMApzcHBfc2Vhc2NhcGVfbm9ybWFsaXplZF9maWx0IDwtIHNwcF9zZWFzY2FwZV9ub3JtYWxpemVkWyFzcHBfc2Vhc2NhcGVfbm9ybWFsaXplZCRYOC5kYXkuc2Vhc2NhcGVzICVpbiUgZXhjbHVkZV9zZWFzY2FwZXMsIF0KCmJiIDwtIGdncGxvdChzcHBfc2Vhc2NhcGVfbm9ybWFsaXplZF9maWx0LCBhZXMoeCA9IGZhY3RvcihYOC5kYXkuc2Vhc2NhcGVzKSwgeSA9IHNwcF9ub3JtYWxpemVkLCBmaWxsID0gZmFjdG9yKFg4LmRheS5zZWFzY2FwZXMpKSkgKwogIGdlb21fYmFyKGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjcsIHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gY3VzdG9tX2NvbG9ycykgKwogIGxhYnMoeCA9ICJTZWFzY2FwZSBDbGFzcyIsIHkgPSAiQ291bnRzIC8gU2Vhc2NhcGUgZnJlcSAqIDEwMDAiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICAjICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKICAjICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpICsKICAjIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpLAogICMgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpICsKICAjIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpICsKICBndWlkZXMoKQpiYgoKYGBgCgojIFBDIHBsb3QKYGBge3J9CmxpYnJhcnkoZ2dhbHQpCgojIFBlcmZvcm0gcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyBvbiB0aGUgY291bnQgZGF0YQoKIyBmb3IgaHlkcm9ncmFwaHkKc2VsX3ZhcnMgPC0gYygKICAiWDguZGF5LnNlYXNjYXBlcyIsCiAgInNhbGluaXR5IiwKICAiQXZnLmNobC5hLi51Zy5MLiIsCiAgIlBPNC4uLnVNLiIsCiAgIk5PMy5OTzIuLnVNLiIsCiAgIk5INC4uLnVNLiIsCiAgInRlbXAuLmRlZ0MuIikKCiMgZm9yIHRheG9ub215CiMgc2VsX3ZhcnMgPC0gYygiWDguZGF5LnNlYXNjYXBlcyIsCiMgICAgICAgICAgICAgICAiQWNhbnRoYXJlYSIsCiMgICAgICAgICAgICAgICAiQ29wZXBvZHMiLAojICAgICAgICAgICAgICAgIkVjaGlub2Rlcm1zIiwKIyAgICAgICAgICAgICAgICJKZWxsaWVzIiwKIyAgICAgICAgICAgICAgICJMYXJ2YWNlYW5zIiwKIyAgICAgICAgICAgICAgICJQb2x5Y2hhZXRzIiwKIyAgICAgICAgICAgICAgICJDaGFldG9nbmF0aHMiLAojICAgICAgICAgICAgICAgIlB0ZXJvcG9kcyIpCgojIHNlbF92YXJzIDwtIGMoIlg4LmRheS5zZWFzY2FwZXMiLAojICAgICAgICAgICAgICAgIkNlcmF0aXVtIiwKIyAgICAgICAgICAgICAgICJDaGFldG9jZXJvcyIsCiMgICAgICAgICAgICAgICAiQ2hhaW4yIiwKIyAgICAgICAgICAgICAgICJDaGFpbjMiLAojICAgICAgICAgICAgICAgIkd1aW5hcmRpYSIsCiMgICAgICAgICAgICAgICAiTmVvY2FseXB0cmVsbGEiLAojICAgICAgICAgICAgICAgIlRyaWNobyIpCgojIHNlbF92YXJzIDwtIGMoIlg4LmRheS5zZWFzY2FwZXMiLAojICAgICAgICAgICAgICAgIkFjYW50aGFyZWEiLAojICAgICAgICAgICAgICAgIkNvcGVwb2RzIiwKIyAgICAgICAgICAgICAgICJFY2hpbm9kZXJtcyIsCiMgICAgICAgICAgICAgICAiSmVsbGllcyIsCiMgICAgICAgICAgICAgICAiTGFydmFjZWFucyIsCiMgICAgICAgICAgICAgICAiUG9seWNoYWV0cyIsCiMgICAgICAgICAgICAgICAiQ2hhZXRvZ25hdGhzIiwKIyAgICAgICAgICAgICAgICJQdGVyb3BvZHMiLAojICAgICAgICAgICAgICAgIkNlcmF0aXVtIiwKIyAgICAgICAgICAgICAgICJDaGFldG9jZXJvcyIsCiMgICAgICAgICAgICAgICAiQ2hhaW4yIiwKIyAgICAgICAgICAgICAgICJDaGFpbjMiLAojICAgICAgICAgICAgICAgIkd1aW5hcmRpYSIsCiMgICAgICAgICAgICAgICAiTmVvY2FseXB0cmVsbGEiLAojICAgICAgICAgICAgICAgIlRyaWNobyIpCgojIGZpbHRlciBvdXQgcm93cyB3aXRoIE5BIHZhbHVlcyBpbiBjb2x1bW4gc2Vhc2NhcGVzCiMgZGZfZmlsdGVyZWRfcGNhIDwtIHRheGFfbWV0YVtjb21wbGV0ZS5jYXNlcyh0YXhhX21ldGEkWDguZGF5LnNlYXNjYXBlcyksIF0KZGZfZmlsdGVyZWRfcGNhIDwtIHRheGFfbWV0YVtjb21wbGV0ZS5jYXNlcyh0YXhhX21ldGFbLCBzZWxfdmFyc10pLCBdCiMgZXhjbHVkZV9zZWFzY2FwZXMgPC0gYyg1LCA3LCAxMSkKZXhjbHVkZV9zZWFzY2FwZXMgPC0gMApmaWx0X2RmX3BjYSA8LSBkZl9maWx0ZXJlZF9wY2FbIWRmX2ZpbHRlcmVkX3BjYSRYOC5kYXkuc2Vhc2NhcGVzICVpbiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhjbHVkZV9zZWFzY2FwZXMsIHNlbF92YXJzXQoKIyBTZWxlY3QgbnVtZXJpYyBjb2x1bW5zIGluIGZpbHRfZGZfcGNhCm51bWVyaWNfY29scyA8LSBzYXBwbHkoZmlsdF9kZl9wY2EsIGlzLm51bWVyaWMpCiMgSWRlbnRpZnkgbnVtZXJpYyBjb2x1bW5zIGV4Y2VwdCB0aGUgZmlyc3Qgb25lCm51bWVyaWNfY29scyA8LSAyOm5jb2woZmlsdF9kZl9wY2EpCgojIFRyYW5zZm9ybSBudW1lcmljIGNvbHVtbnMgdG8gbG9nIHNjYWxlCmZpbHRfZGZfcGNhW251bWVyaWNfY29sc10gPC0gbGFwcGx5KGZpbHRfZGZfcGNhW251bWVyaWNfY29sc10sIGZ1bmN0aW9uKHgpIGxvZyh4ICsgMSkpCgoKIyBwY2EgPC0gcHJjb21wKGZpbHRfZGZfcGNhWywgYygic2FsaW5pdHkiLCAiQXZnLmNobC5hLi51Zy5MLiIsICJQTzQuLi51TS4iLCAiTk8zLk5PMi4udU0uIildLCBzY2FsZS4gPSBUUlVFKQpwY2EgPC0gcHJjb21wKGZpbHRfZGZfcGNhWywgLTFdLCBzY2FsZS4gPSBUUlVFKQoKIyBFeHRyYWN0IFBDMSBhbmQgUEMyIHNjb3JlcyBmb3IgZWFjaCBzYW1wbGluZyBldmVudAojIHBjX3Njb3JlcyA8LSBkYXRhLmZyYW1lKHNlYXNjYXBlID0gZGZfc3Vic2V0JFg4LmRheS5zZWFzY2FwZXMsICMgZm9yIHRheG9ub21pYyBhbmFseXNpcyAKcGNfc2NvcmVzIDwtIGRhdGEuZnJhbWUoc2Vhc2NhcGUgPSBhcy5jaGFyYWN0ZXIoZmlsdF9kZl9wY2EkWDguZGF5LnNlYXNjYXBlcyksICMgZm9yIGh5ZHJvZ3JhcGh5CiAgICAgICAgICAgICAgICAgICAgICAgIFBDMSA9IHBjYSR4WywgMV0sIAogICAgICAgICAgICAgICAgICAgICAgICBQQzIgPSBwY2EkeFssIDJdKQoKIyBDcmVhdGUgdGhlIHBsb3QKcGNfc2NvcmVzJHNlYXNjYXBlIDwtIGZhY3RvcihwY19zY29yZXMkc2Vhc2NhcGUsIGxldmVscyA9IGMoIjMiLCAiNSIsICI3IiwgIjExIiwgIjEzIiwgIjE1IiwgIjIxIiwgIjI3IikpCmJiIDwtIGdncGxvdChwY19zY29yZXMsIGFlcyh4ID0gUEMxLCB5ID0gUEMyLCBjb2xvciA9IHNlYXNjYXBlKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIGxhYnMoeCA9ICJQQzEiLCB5ID0gIlBDMiIsIGNvbG9yID0gIlNlYXNjYXBlIikgCgojIGFkZCBjaXJjbGUgYXJvdW5kIGNsdXN0ZXIgb2YgZGF0YSBwb2ludHMKIyBjdXN0b21fY29sb3JzX3BjYSA8LSBjdXN0b21fcGFsX2hleFtjKDEsIDUsIDYsIDcsIDgpXQpjdXN0b21fY29sb3JzX3BjYSA8LSBjdXN0b21fY29sb3JzCgp5eSA8LSBnZ3Bsb3QocGNfc2NvcmVzLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3IgPSBzZWFzY2FwZSkpICsgCiAgZ2VvbV9wb2ludCgpICsKICBzdGF0X2VsbGlwc2UoYWVzKGZpbGwgPSBzZWFzY2FwZSksIGxldmVsID0gMC45MCwgZ2VvbSA9ICJwb2x5Z29uIiwgYWxwaGEgPSAwLjMsIGNvbG9yID0gImJsYWNrIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzX3BjYSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnNfcGNhKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB4bGltKC0yLDIpICsKICB5bGltKC0yLDIpICsKICAjIHhsaW0oLTEsMSkgKwogICMgeWxpbSgtMSwxKSArCiAgZ2VvbV9wb2ludChzaXplPTIpICsKICBndWlkZXMoY29sb3VyID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZT0yKSkpICsgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMiksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMikpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpKQoKeXkKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojICMgQ3JlYXRlIFBDQSB3aXRoIGVpZ2VudmVjdG9ycwojIEV4dHJhY3QgcHJpbmNpcGFsIGNvbXBvbmVudCBzY29yZXMKcGNfc2NvcmVzMiA8LSBwY2EkeAojIEV4dHJhY3QgZWlnZW52ZWN0b3JzCmVpZ2VudmVjdG9ycyA8LSBwY2Ekcm90YXRpb24KIyBDYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2UgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGVhY2ggcHJpbmNpcGFsIGNvbXBvbmVudAp0b3RhbF92YXJpYW5jZSA8LSBzdW0ocGNhJHNkZXZeMikKcGNfdmFyX3BlcmNlbnQgPC0gcm91bmQoMTAwICogKHBjYSRzZGV2XjIpIC8gdG90YWxfdmFyaWFuY2UsIDEpCgojIENvbnZlcnQgWDguZGF5LnNlYXNjYXBlcyB0byBhIGZhY3RvcgpmaWx0X2RmX3BjYSRYOC5kYXkuc2Vhc2NhcGVzIDwtIGFzLmZhY3RvcihmaWx0X2RmX3BjYSRYOC5kYXkuc2Vhc2NhcGVzKQoKIyAjIEZvciBoeWRyb2dyYXBoeQpxcSA8LSBnZ3Bsb3QoZmlsdF9kZl9wY2EsIGFlcyh4ID0gcGNfc2NvcmVzMlssMV0sIHkgPSBwY19zY29yZXMyWywyXSwgY29sb3IgPSBYOC5kYXkuc2Vhc2NhcGVzKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gMC43KSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGN1c3RvbV9jb2xvcnNfcGNhKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBlaWdlbnZlY3RvcnNbMSwgMV0sIHllbmQgPSBlaWdlbnZlY3RvcnNbMiwgMV0pLAogICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4xLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIHZlY3RvciBmb3IgUEMxCiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBlaWdlbnZlY3RvcnNbMSwgMl0sIHllbmQgPSBlaWdlbnZlY3RvcnNbMiwgMl0pLAogICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4xLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzIKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCAzXSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCAzXSksCiAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjEsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAjIEFkZCB2ZWN0b3IgZm9yIFBDMwogIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDRdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDRdKSwKICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMSwgImluY2hlcyIpKSwgY29sb3IgPSAiYmxhY2siKSArICMgQWRkIHZlY3RvciBmb3IgUEM0CiAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBlaWdlbnZlY3RvcnNbMSwgNV0sIHllbmQgPSBlaWdlbnZlY3RvcnNbMiwgNV0pLAogICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4xLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzUKICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCA2XSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCA2XSksCiAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjEsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAjIEFkZCB2ZWN0b3IgZm9yIFBDNgogIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCAxXSwgeSA9IGVpZ2VudmVjdG9yc1syLCAxXSwgCiAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzEgKCIsIHBjX3Zhcl9wZXJjZW50WzFdLCAiJTogU2FsaW5pdHkpIiwgc2VwID0gIiIpKSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEMxCiAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDJdLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDJdLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDMiAoIiwgcGNfdmFyX3BlcmNlbnRbMl0sICIlOiBDaGxhKSIsIHNlcCA9ICIiKSksCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgbGFiZWwgZm9yIFBDMgogIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCAzXSwgeSA9IGVpZ2VudmVjdG9yc1syLCAzXSwgCiAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzMgKCIsIHBjX3Zhcl9wZXJjZW50WzNdLCAiJTogUGhvc3BoYXRlKSIsIHNlcCA9ICIiKSksCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgbGFiZWwgZm9yIFBDMwogIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCA0XSwgeSA9IGVpZ2VudmVjdG9yc1syLCA0XSwgCiAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzQgKCIsIHBjX3Zhcl9wZXJjZW50WzRdLCAiJTogTk94KSIsIHNlcCA9ICIiKSksCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgbGFiZWwgZm9yIFBDNAogIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCA1XSwgeSA9IGVpZ2VudmVjdG9yc1syLCA1XSwgCiAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzUgKCIsIHBjX3Zhcl9wZXJjZW50WzVdLCAiJTogQW1tb25pdW0pIiwgc2VwID0gIiIpKSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEM1CiAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDZdLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDZdLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDNiAoIiwgcGNfdmFyX3BlcmNlbnRbNl0sICIlOiBUZW1wZXJhdHVyZSkiLCBzZXAgPSAiIikpLAogICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzYKICBsYWJzKHggPSAiUEMxIiwgeSA9ICJQQzIiLCBjb2xvciA9ICJTZWFzY2FwZSBjbGFzcyIpICsKICB4bGltKC0xLjUsIDEuNSkgKwogIHlsaW0oLTEuNSwgMS41KSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9NSkpKSArIAogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwgICMgU2V0IFgtYXhpcyBsYWJlbCBmb250IHNpemUKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpCnFxCgoKIyAjICMgRm9yIHBoeXRvcGxhbmt0b24KIyBxcTIgPC0gZ2dwbG90KGZpbHRfZGZfcGNhLCBhZXMoeCA9IHBjX3Njb3JlczJbLDFdLCB5ID0gcGNfc2NvcmVzMlssM10sIGNvbG9yID0gWDguZGF5LnNlYXNjYXBlcykpICsKIyAgIGdlb21fcG9pbnQoc2l6ZSA9IDQpICsKIyAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjdXN0b21fY29sb3JzX3BjYSkgKwojICAgZ2VvbV9zZWdtZW50KGFlcyh4ID0gMCwgeSA9IDAsIHhlbmQgPSBlaWdlbnZlY3RvcnNbMSwgMV0sIHllbmQgPSBlaWdlbnZlY3RvcnNbMiwgMV0pLAojICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3cobGVuZ3RoID0gdW5pdCgwLjIsICJpbmNoZXMiKSksIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgdmVjdG9yIGZvciBQQzEKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDJdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDJdKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzIKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDNdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDNdKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzMKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDRdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDRdKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzQKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDVdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDVdKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzUKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDZdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDZdKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzYKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDddLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDddKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgIyBBZGQgdmVjdG9yIGZvciBQQzcKIyAgIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCAxXSwgeSA9IGVpZ2VudmVjdG9yc1syLCAxXSwgCiMgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDMSAoIiwgcGNfdmFyX3BlcmNlbnRbMV0sICIlOiBDZXJhdGl1bSkiLCBzZXAgPSAiIikpLAojICAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgbGFiZWwgZm9yIFBDMQojICAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDJdLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDJdLCAKIyAgICAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiUEMyICgiLCBwY192YXJfcGVyY2VudFsyXSwgIiU6IENoYWV0b2Nlcm9zKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEMyCiMgICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgM10sIHkgPSBlaWdlbnZlY3RvcnNbMiwgM10sIAojICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzMgKCIsIHBjX3Zhcl9wZXJjZW50WzNdLCAiJTogRGlhdG9tcykiLCBzZXAgPSAiIikpLAojICAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgbGFiZWwgZm9yIFBDMwojICAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDRdLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDRdLCAKIyAgICAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiUEM0ICgiLCBwY192YXJfcGVyY2VudFs0XSwgIiU6IERpYXRvbXMyKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEM0CiMgICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgNV0sIHkgPSBlaWdlbnZlY3RvcnNbMiwgNV0sIAojICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzUgKCIsIHBjX3Zhcl9wZXJjZW50WzVdLCAiJTogR3VpbmFyZGlhKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEM1CiMgICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgNl0sIHkgPSBlaWdlbnZlY3RvcnNbMiwgNl0sIAojICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzYgKCIsIHBjX3Zhcl9wZXJjZW50WzZdLCAiJTogTmVvY2FseXB0cmVsbGEpIiwgc2VwID0gIiIpKSwKIyAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzYKIyAgIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCA3XSwgeSA9IGVpZ2VudmVjdG9yc1syLCA3XSwgCiMgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDNyAoIiwgcGNfdmFyX3BlcmNlbnRbN10sICIlOiBUcmljaG9kZXNtaXVtKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEM3CiMgICBsYWJzKHggPSAiUEMxIiwgeSA9ICJQQzMiLCBjb2xvciA9ICJYOC5kYXkuc2Vhc2NhcGVzIikgKwojICAgeGxpbSgtMSwgMSkgKwojICAgeWxpbSgtMSwgMSkgKwojICAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9MikpKSArIAojICAgdGhlbWVfY2xhc3NpYygpICsKIyAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMiksICAjIFNldCBYLWF4aXMgbGFiZWwgZm9udCBzaXplCiMgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpKSArCiMgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSwKIyAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpKSArCiMgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpKSAKIyBxcTIKIyAKIyAKIyAjICMgRm9yIHpvb3BsYW5rdG9uCiMgcXEzIDwtIGdncGxvdChmaWx0X2RmX3BjYSwgYWVzKHggPSBwY19zY29yZXMyWywxXSwgeSA9IHBjX3Njb3JlczJbLDJdLCBjb2xvciA9IFg4LmRheS5zZWFzY2FwZXMpKSArCiMgICBnZW9tX3BvaW50KHNpemUgPSA0KSArCiMgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY3VzdG9tX2NvbG9yc19wY2EpICsKIyAgIGdlb21fc2VnbWVudChhZXMoeCA9IDAsIHkgPSAwLCB4ZW5kID0gZWlnZW52ZWN0b3JzWzEsIDFdLCB5ZW5kID0gZWlnZW52ZWN0b3JzWzIsIDFdKSwKIyAgICAgICAgICAgICAgICBhcnJvdyA9IGFycm93KGxlbmd0aCA9IHVuaXQoMC4yLCAiaW5jaGVzIikpLCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIHZlY3RvciBmb3IgUEMxCiMgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCAyXSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCAyXSksCiMgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSwgY29sb3IgPSAiYmxhY2siKSArICMgQWRkIHZlY3RvciBmb3IgUEMyCiMgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCAzXSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCAzXSksCiMgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSwgY29sb3IgPSAiYmxhY2siKSArICMgQWRkIHZlY3RvciBmb3IgUEMzCiMgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCA0XSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCA0XSksCiMgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSwgY29sb3IgPSAiYmxhY2siKSArICMgQWRkIHZlY3RvciBmb3IgUEM0CiMgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCA1XSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCA1XSksCiMgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSwgY29sb3IgPSAiYmxhY2siKSArICMgQWRkIHZlY3RvciBmb3IgUEM1CiMgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCA2XSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCA2XSksCiMgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSwgY29sb3IgPSAiYmxhY2siKSArICMgQWRkIHZlY3RvciBmb3IgUEM2CiMgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCA3XSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCA3XSksCiMgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSwgY29sb3IgPSAiYmxhY2siKSArICMgQWRkIHZlY3RvciBmb3IgUEM3CiMgICBnZW9tX3NlZ21lbnQoYWVzKHggPSAwLCB5ID0gMCwgeGVuZCA9IGVpZ2VudmVjdG9yc1sxLCA4XSwgeWVuZCA9IGVpZ2VudmVjdG9yc1syLCA4XSksCiMgICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyhsZW5ndGggPSB1bml0KDAuMiwgImluY2hlcyIpKSwgY29sb3IgPSAiYmxhY2siKSArICMgQWRkIHZlY3RvciBmb3IgUEM4CiMgICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgMV0sIHkgPSBlaWdlbnZlY3RvcnNbMiwgMV0sIAojICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzEgKCIsIHBjX3Zhcl9wZXJjZW50WzFdLCAiJTogQWNhbnRoYXJlYSkiLCBzZXAgPSAiIikpLAojICAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgbGFiZWwgZm9yIFBDMQojICAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDJdLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDJdLCAKIyAgICAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiUEMyICgiLCBwY192YXJfcGVyY2VudFsyXSwgIiU6IENvcGVwb2RzKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEMyCiMgICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgM10sIHkgPSBlaWdlbnZlY3RvcnNbMiwgM10sIAojICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzMgKCIsIHBjX3Zhcl9wZXJjZW50WzNdLCAiJTogRWNoaW5vZGVybXMpIiwgc2VwID0gIiIpKSwKIyAgICAgICAgICAgICB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41LCBjb2xvciA9ICJibGFjayIpICsgICMgQWRkIGxhYmVsIGZvciBQQzMKIyAgIGdlb21fdGV4dChhZXMoeCA9IGVpZ2VudmVjdG9yc1sxLCA0XSwgeSA9IGVpZ2VudmVjdG9yc1syLCA0XSwgCiMgICAgICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIlBDNCAoIiwgcGNfdmFyX3BlcmNlbnRbNF0sICIlOiBKZWxsaWVzKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEM0CiMgICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgNV0sIHkgPSBlaWdlbnZlY3RvcnNbMiwgNV0sIAojICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzUgKCIsIHBjX3Zhcl9wZXJjZW50WzVdLCAiJTogTGFydmFjZWFucykiLCBzZXAgPSAiIikpLAojICAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgaGp1c3QgPSAwLjUsIGNvbG9yID0gImJsYWNrIikgKyAgIyBBZGQgbGFiZWwgZm9yIFBDNQojICAgZ2VvbV90ZXh0KGFlcyh4ID0gZWlnZW52ZWN0b3JzWzEsIDZdLCB5ID0gZWlnZW52ZWN0b3JzWzIsIDZdLCAKIyAgICAgICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiUEM2ICgiLCBwY192YXJfcGVyY2VudFs2XSwgIiU6IFBvbHljaGFldGVzKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEM2CiMgICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgN10sIHkgPSBlaWdlbnZlY3RvcnNbMiwgN10sIAojICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzcgKCIsIHBjX3Zhcl9wZXJjZW50WzddLCAiJTogQ2hhZXRvZ25hdGhzKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEM3CiMgICBnZW9tX3RleHQoYWVzKHggPSBlaWdlbnZlY3RvcnNbMSwgOF0sIHkgPSBlaWdlbnZlY3RvcnNbMiwgOF0sIAojICAgICAgICAgICAgICAgICBsYWJlbCA9IHBhc3RlKCJQQzggKCIsIHBjX3Zhcl9wZXJjZW50WzhdLCAiJTogUHRlcm9wb2RzKSIsIHNlcCA9ICIiKSksCiMgICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSwgY29sb3IgPSAiYmxhY2siKSArICAjIEFkZCBsYWJlbCBmb3IgUEM4CiMgICBsYWJzKHggPSAiUEMxIiwgeSA9ICJQQzIiLCBjb2xvciA9ICJYOC5kYXkuc2Vhc2NhcGVzIikgKwojICAgeGxpbSgtMSwgMSkgKwojICAgeWxpbSgtMSwgMSkgKwojICAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9MikpKSArIAojICAgdGhlbWVfY2xhc3NpYygpICsKIyAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMiksICAjIFNldCBYLWF4aXMgbGFiZWwgZm9udCBzaXplCiMgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpKSArCiMgICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDMyKSwKIyAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpKSArCiMgICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzIpKSAKIyBxcTMKCmBgYAoKCiMgTWFwIG92ZXJhbGwgV0VJR0hURUQgbWVhbiBjb25jZW50cmF0aW9uIHZhbHVlcyBvZiBzZWxlY3RlZCB0YXhhIGJ5IHRyYW5zZWN0CmBgYHtyfQpsaWJyYXJ5KGdnT2NlYW5NYXBzKQpsaWJyYXJ5KGxlYWZsZXQpCmxpYnJhcnkocGF0Y2h3b3JrKQoKIyBGb3Igem9vcGxhbmt0b24KdGF4YV9tZXRhX2NvbmNlbnRyYXRpb24gPC0gdGF4YV9tZXRhICU+JQogIG11dGF0ZShhY3Jvc3MoYyhBY2FudGhhcmVhLENoYWV0b2duYXRocyxPc3RyYWNvZHMsQ29wZXBvZHMsRGVjYXBvZHMsRWNoaW5vZGVybXMsSmVsbGllcywKICAgIExhcnZhY2VhbnMsUG9seWNoYWV0cyxQdGVyb3BvZHMpLCB+IC4vdG90YWxfdm9sX3NhbXBsZWQgKiAxZTYpKSAlPiUKICBzZWxlY3QoU3RhdGlvbiwgZGVjX2xhdCwgZGVjX2xvbiwgeWVhciwgbW9udGgsIGRhdGUsIEF2Zy5jaGwuYS4udWcuTC4sCiAgICAgICAgIHRlbXAuLmRlZ0MuLCBzYWxpbml0eSwgdG90YWxfdm9sX3NhbXBsZWQsCiAgICAgICAgIEFjYW50aGFyZWEsQ2hhZXRvZ25hdGhzLE9zdHJhY29kcyxDb3BlcG9kcyxEZWNhcG9kcyxFY2hpbm9kZXJtcywKICAgICAgICAgSmVsbGllcyxMYXJ2YWNlYW5zLFBvbHljaGFldHMsUHRlcm9wb2RzKSAlPiUKICBmaWx0ZXIoIWlzLm5hKHRvdGFsX3ZvbF9zYW1wbGVkKSkKCiMgIyBGb3IgcGh5dG9wbGFua3RvbgojIHRheGFfbWV0YV9jb25jZW50cmF0aW9uIDwtIHRheGFfbWV0YSAlPiUKIyAgIG11dGF0ZShhY3Jvc3MoYyhDZW50cmljLENlcmF0aXVtLENoYWV0b2Nlcm9zLENoYWluMixDaGFpbjMsR3VpbmFyZGlhLE5lb2NhbHlwdHJlbGxhLAojICAgICAgICAgICAgICAgICAgIE5vY3RpbHVjYSxUcmljaG8pLCB+IC4vdG90YWxfdm9sX3NhbXBsZWQgKiAxZTYpKSAlPiUKIyAgIHNlbGVjdChTdGF0aW9uLCBkZWNfbGF0LCBkZWNfbG9uLCB5ZWFyLCBtb250aCwgZGF0ZSwgQXZnLmNobC5hLi51Zy5MLiwKIyAgICAgICAgICB0ZW1wLi5kZWdDLiwgc2FsaW5pdHksdG90YWxfdm9sX3NhbXBsZWQsCiMgICAgICAgICAgQ2VudHJpYyxDZXJhdGl1bSxDaGFldG9jZXJvcyxDaGFpbjIsQ2hhaW4zLEd1aW5hcmRpYSxOZW9jYWx5cHRyZWxsYSwKIyAgICAgICAgICAgICAgICAgICBOb2N0aWx1Y2EsVHJpY2hvKSAlPiUKIyAgIGZpbHRlcighaXMubmEodG90YWxfdm9sX3NhbXBsZWQpKQoKIyBMaXN0IG9mIGNsYXNzIG5hbWVzIHRvIGxvb3AgdGhyb3VnaApjbGFzc19uYW1lcyA8LSBjKCJBY2FudGhhcmVhIiwgIkNoYWV0b2duYXRocyIsICJPc3RyYWNvZHMiLCAiQ29wZXBvZHMiLCAiRGVjYXBvZHMiLCAiRWNoaW5vZGVybXMiLCAiSmVsbGllcyIsICJMYXJ2YWNlYW5zIiwgIlBvbHljaGFldHMiLCAiUHRlcm9wb2RzIikKCmNsYXNzX25hbWVzIDwtICJBY2FudGhhcmVhIgoKcGF0aF9zZmVyX2xpc3QgPC0gIn4vTGlicmFyeS9DbG91ZFN0b3JhZ2UvR29vZ2xlRHJpdmUtZW5yaXF1ZW1vbnRlczAxQGdtYWlsLmNvbS9NeSBEcml2ZS9HRHJpdmUvcHJvcG9zYWxzLzIwMjJfMDJfTXVsdGlTdHJlc3Nvcl9OT0FBL21vZHVsZV8xIgoKc2Zlcl9jdXJhdGVkIDwtIGxpc3QuZmlsZXMocGF0aF9zZmVyX2xpc3QsIHBhdHRlcm4gPSAic2Zlcl9zdGF0aW9uc19jdXJhdGVkLmNzdiIsIGZ1bGwubmFtZXMgPSBUUlVFKQpzZmVyX3N0YV9saXN0IDwtIHJlYWQuY3N2KHNmZXJfY3VyYXRlZCwgaGVhZGVyID0gVFJVRSkKCiMgTG9vcCBvdmVyIGVhY2ggY2xhc3MgbmFtZSB0byBnZW5lcmF0ZSBtYXBzCiMgZm9yIChjbGFzc19uYW1lIGluIGNsYXNzX25hbWVzKSB7CgogICAgIyAjIEZpbHRlciBjb25jZW50cmF0aW9uIGRhdGEgZm9yIHRoZSBjdXJyZW50IGNsYXNzIGFuZCBtZXJnZXMgY3VyYXRlZCBzdGF0aW9uIGxpc3Qgd2l0aCB0aGUgY29uY2VudHJhdGlvbiBkZgogICAgY29uY2VudHJhdGlvbl9kZiA8LSB0YXhhX21ldGFfY29uY2VudHJhdGlvbiAlPiUKICAgICAgbGVmdF9qb2luKHNmZXJfc3RhX2xpc3QgJT4lIGZpbHRlcihzdGF0aW9uX2NsYXNzICVpbiUgIkMiKSwgYnkgPSBjKCdTdGF0aW9uJyA9ICdzdGF0aW9uX2lkJykpCiAgICAKICAgICMgQ29tcHV0ZSB3ZWlnaHRlZCBhdmVyYWdlIGxhdC9sb25zIGFuZCBtZWFuIHZhcmlhYmxlIHZhbHVlcyBmb3IgdGhlIGN1cnJlbnQgY2xhc3MKICAgIHRheGFfY29uY2VudHJhdGlvbl9hdmcgPC0gY29uY2VudHJhdGlvbl9kZiAlPiUKICAgICAgZ3JvdXBfYnkobGluZV9pZCkgJT4lCiAgICAgIG11dGF0ZSh3ZWlnaHQgPSBnZXQoY2xhc3NfbmFtZSkgLyBzdW0oZ2V0KGNsYXNzX25hbWUpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICAgICAgc3VtbWFyaXNlKGxvbmdpdHVkZSA9IHdlaWdodGVkLm1lYW4oZGVjX2xvbiwgdyA9IHdlaWdodCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgIGxhdGl0dWRlID0gd2VpZ2h0ZWQubWVhbihkZWNfbGF0LCB3ID0gd2VpZ2h0LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgc2VsX3RheGFfbWVhbiA9IG1lYW4oZ2V0KGNsYXNzX25hbWUpLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgc2VsX3RheGFfc2QgPSBzZChnZXQoY2xhc3NfbmFtZSksIG5hLnJtID0gVFJVRSkpCgogICAgIyBQcmVwYXJlIGRhdGEgZm9yIG1hcHBpbmcKICAgIGR0IDwtIGRhdGEuZnJhbWUoCiAgICAgIGxvbiA9IHRheGFfY29uY2VudHJhdGlvbl9hdmckbG9uZ2l0dWRlLCAKICAgICAgbGF0ID0gdGF4YV9jb25jZW50cmF0aW9uX2F2ZyRsYXRpdHVkZSwgCiAgICAgIG1lYW5fcGFyYW0gPSB0YXhhX2NvbmNlbnRyYXRpb25fYXZnJHNlbF90YXhhX21lYW4sCiAgICAgIHNkX3BhcmFtID0gdGF4YV9jb25jZW50cmF0aW9uX2F2ZyRzZWxfdGF4YV9zZCkKICAgIAogICAgIyBBZGQgc3RhdGlvbiBtYXJrZXJzCiAgICBzdGF0aW9uX21hcmtlcnMgPC0gc2Zlcl9zdGFfbGlzdCAlPiUgZmlsdGVyKHN0YXRpb25fY2xhc3MgJWluJSAiQyIpCiAgICAKICAgICMgQ3JlYXRlIHRoZSBtYXAKICAgIGNvbmNlbnRyYXRpb25fbWFwIDwtIGJhc2VtYXAobGltaXRzID0gYygtODYsIC03OS41LCAyNCwgMjguNSksIGJhdGh5bWV0cnkgPSBUUlVFKSArCiAgICAgIGdlb21fcG9pbnQoZGF0YSA9IGR0LCBhZXMoeCA9IGxvbiwgeSA9IGxhdCwgc2l6ZSA9IG1lYW5fcGFyYW0sIGNvbG9yID0gc2RfcGFyYW0pKSArCiAgICAgIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICJ5ZWxsb3ciLCBoaWdoID0gInJlZCIsIG5hLnZhbHVlID0gTkEsIG5hbWUgPSAiU3RhbmRhcmQgRGV2aWF0aW9uIikgKwogICAgICBnZW9tX3BvaW50KGRhdGEgPSBzdGF0aW9uX21hcmtlcnMsIGFlcyh4ID0gbWVhbl9sb24sIHkgPSBtZWFuX2xhdCksIGNvbG9yID0gImJsYWNrIiwgc2hhcGUgPSAzLCBzaXplID0gMS41KSArCiAgICAgIHNjYWxlX3NpemVfY29udGludW91cyhuYW1lID0gIkF2ZXJhZ2UgY29uY2VudHJhdGlvbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygyNTAsIDUwMCwgMTAwMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5nZSA9IGMoMCwgMTApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChjb2xvciA9ICJibGFjayIsIGZpbGwgPSAid2hpdGUiKSkpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBnZ3RpdGxlKHBhc3RlKGNsYXNzX25hbWUpKQogICAgICAKICAgICMgT3B0aW9uYWxseSwgc2F2ZSBlYWNoIG1hcCBhcyBhbiBpbWFnZQogICAgZ2dzYXZlKHBhc3RlMCgiY29uY2VudHJhdGlvbl9tYXBfIiwgY2xhc3NfbmFtZSwgIi5wbmciKSwgcGxvdCA9IGNvbmNlbnRyYXRpb25fbWFwLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCiMgfQpgYGAKCgojIE1hcCBvdmVyYWxsIG1lYW4gY29uY2VudHJhdGlvbiB2YWx1ZXMgb2Ygc2VsZWN0ZWQgdGF4YSBwZXIgc3RhdGlvbgpgYGB7cn0KIyBDb21wdXRlIG1lYW4gY29uY2VudHJhdGlvbiBvZiBzZWxlY3RlZCBncm91cHMgcGVyIHN0YXRpb24KdGF4YV9jb25jZW50cmF0aW9uX2F2Z19zdGF0aW9uIDwtIHRheGFfbWV0YV9jb25jZW50cmF0aW9uICU+JQogIGdyb3VwX2J5KFN0YXRpb24pICU+JQogIHN1bW1hcmlzZShsb25naXR1ZGVfc3RhdGlvbiA9IG1lYW4oZGVjX2xvbiwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbGF0aXR1ZGVfc3RhdGlvbiA9IG1lYW4oZGVjX2xhdCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgc2VsX3RheGFfbWVhbl9zdGF0aW9uID0gbWVhbihUcmljaG8sIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHNlbF90YXhhX3NkX3N0YXRpb24gPSBzZChUcmljaG8sIG5hLnJtID0gVFJVRSkpCgojICMgTWFwIHdpdGggYmF0aHltZXRyeQogICMgT3ZlcmxheSBjb2xvci1zY2FsZWQgZG90cwpkdF9zdGF0aW9uIDwtIGRhdGEuZnJhbWUoCiAgbG9uX3N0YXRpb24gPSB0YXhhX2NvbmNlbnRyYXRpb25fYXZnX3N0YXRpb24kbG9uZ2l0dWRlX3N0YXRpb24sIAogIGxhdF9zdGF0aW9uID0gdGF4YV9jb25jZW50cmF0aW9uX2F2Z19zdGF0aW9uJGxhdGl0dWRlX3N0YXRpb24sIAogIG1lYW5fcGFyYW1fc3RhdGlvbiA9IHRheGFfY29uY2VudHJhdGlvbl9hdmdfc3RhdGlvbiRzZWxfdGF4YV9tZWFuX3N0YXRpb24sCiAgc2RfcGFyYW1fc3RhdGlvbiA9IHRheGFfY29uY2VudHJhdGlvbl9hdmdfc3RhdGlvbiRzZWxfdGF4YV9zZF9zdGF0aW9uKQoKIyBSZXBsYWNlIE5BIGJ5IHplcm9zCmR0X3N0YXRpb24gPC0gZHRfc3RhdGlvbiAlPiUKICBtdXRhdGUoc2RfcGFyYW1fc3RhdGlvbiA9IHJlcGxhY2VfbmEoc2RfcGFyYW1fc3RhdGlvbiwgMCkpCgpjb25jZW50cmF0aW9uX21hcF9zdGF0aW9uIDwtIGJhc2VtYXAobGltaXRzID0gYygtODYsIC03OS41LCAyNCwgMjguNSksIGJhdGh5bWV0cnkgPSBUUlVFKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZHRfc3RhdGlvbiwgYWVzKHggPSBsb25fc3RhdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBsYXRfc3RhdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSBtZWFuX3BhcmFtX3N0YXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHNkX3BhcmFtX3N0YXRpb24pKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gInllbGxvdyIsIGhpZ2ggPSAicmVkIiwgbmEudmFsdWUgPSBOQSwgbmFtZSA9ICJTdGFuZGFyZCBEZXZpYXRpb24iKSArCiAgZ2VvbV9wb2ludChkYXRhID0gc3RhdGlvbl9tYXJrZXJzLCBhZXMoeCA9IG1lYW5fbG9uLCB5ID0gbWVhbl9sYXQpLCBjb2xvciA9ICJibGFjayIsIHNoYXBlID0gMywgc2l6ZSA9IDEuNSkgKwogIHNjYWxlX3NpemVfY29udGludW91cyhuYW1lID0gIkF2ZXJhZ2UgY29uY2VudHJhdGlvbiIsIAogICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDI1MCwgNTAwLCA3NTApLAogICAgICAgICAgICAgICAgICAgICAgICByYW5nZSA9IGMoMSwgMTApLAogICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJ3aGl0ZSIpKSkgKwogIHRoZW1lX21pbmltYWwoKSAKY29uY2VudHJhdGlvbl9tYXBfc3RhdGlvbgpgYGAKCgojIE1hcCBtZWFuIGNvdW50IHZhbHVlcyBvZiBzZWxlY3RlZCB0YXhhIGFuZCBzcGVjaWZpZWQgZGF0ZXMKYGBge3J9CiMgIyBnZ09jZWFuTWFwczoKIyBodHRwczovL2Jpb3N0YXRzLXIuZ2l0aHViLmlvL2Jpb3N0YXRzL3dvcmtpbmdJblIvMTQwX21hcHMuaHRtbAojIGh0dHBzOi8vZ2l0aHViLmNvbS9NaWtrb1ZpaHRha2FyaS9nZ09jZWFuTWFwczogVXNlIHRoaXMgb25lOiByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiTWlra29WaWh0YWthcmkvZ2dPY2Vhbk1hcHMiKQojIGluc3RhbGwucGFja2FnZXMoImdnT2NlYW5NYXBzIikgIyAjIFRoaXMgaXMgb3V0ZGF0ZWQsIGRvbid0IHVzZSEhIQpsaWJyYXJ5KGdnT2NlYW5NYXBzKQpsaWJyYXJ5KGxlYWZsZXQpCgojIEZvciBOT0FBIG1hY2hpbmVzCiMgcGF0aF9zZmVyX2xpc3QgPC0gIi9Vc2Vycy9lbnJpcXVlLm1vbnRlcy9MaWJyYXJ5L0Nsb3VkU3RvcmFnZS9Hb29nbGVEcml2ZS1lbnJpcXVlbW9udGVzMDFAZ21haWwuY29tL015IERyaXZlL0dEcml2ZS9wcm9wb3NhbHMvMjAyMl8wMl9NdWx0aVN0cmVzc29yX05PQUEvbW9kdWxlXzEiCiMgRm9yIHBlcnNvbmFsIG1hY2hpbmUKcGF0aF9zZmVyX2xpc3QgPC0gIn4vTGlicmFyeS9DbG91ZFN0b3JhZ2UvR29vZ2xlRHJpdmUtZW5yaXF1ZW1vbnRlczAxQGdtYWlsLmNvbS9NeSBEcml2ZS9HRHJpdmUvcHJvcG9zYWxzLzIwMjJfMDJfTXVsdGlTdHJlc3Nvcl9OT0FBL21vZHVsZV8xIgoKc2Zlcl9jdXJhdGVkIDwtIGxpc3QuZmlsZXMocGF0aF9zZmVyX2xpc3QsIHBhdHRlcm4gPSAic2Zlcl9zdGF0aW9uc19jdXJhdGVkLmNzdiIsIGZ1bGwubmFtZXMgPSBUUlVFKQoKc2Zlcl9zdGFfbGlzdCA8LSByZWFkLmNzdihzZmVyX2N1cmF0ZWQsIGhlYWRlciA9IFRSVUUpCgptZXJnZWRfZGF0YSA8LSB0YXhhX21ldGEgJT4lCiAgbGVmdF9qb2luKHNmZXJfc3RhX2xpc3QgJT4lIGZpbHRlcihzdGF0aW9uX2NsYXNzICVpbiUgIkMiKSwgYnkgPSBjKCdTdGF0aW9uJyA9ICdzdGF0aW9uX2lkJykpCgojIFJlcGxhY2UgMjAyMyBhbmQgMTAgd2l0aCB5b3VyIHNlbGVjdGVkIHllYXIgYW5kIG1vbnRoCnNlbGVjdGVkX3llYXIgPC0gMjAyMwpzZWxlY3RlZF9tb250aCA8LSAxMQoKIyBGaWx0ZXIgcm93cwpmaWx0ZXJlZF90YXhhX21ldGEgPC0gbWVyZ2VkX2RhdGEgJT4lCiAgZmlsdGVyKHllYXIgPT0gc2VsZWN0ZWRfeWVhciwgbW9udGggPT0gc2VsZWN0ZWRfbW9udGgpCgojIGZpbHRlcmVkX3RheGFfbWV0YSA8LSBtZXJnZWRfZGF0YQoKIyBDb21wdXRlIHdlaWdodGVkIGF2ZXJhZ2UgbGF0IGxvbnMgYmFzZWQgb24gc2VsZWN0ZWQgdmFyaWFibGUsIGFuZCBtZWFuIHZhcmlhYmxlIHZhbHVlcyAoZS5nLiwgQ29wZXBvZHMpCnRheGFfYXZnIDwtIGZpbHRlcmVkX3RheGFfbWV0YSAlPiUKICBncm91cF9ieShsaW5lX2lkKSAlPiUKICBtdXRhdGUod2VpZ2h0ID0gR3VpbmFyZGlhIC8gc3VtKEd1aW5hcmRpYSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgc3VtbWFyaXNlKGxvbmdpdHVkZSA9IHdlaWdodGVkLm1lYW4oZGVjX2xvbiwgdyA9IHdlaWdodCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbGF0aXR1ZGUgPSB3ZWlnaHRlZC5tZWFuKGRlY19sYXQsIHcgPSB3ZWlnaHQsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHNlbF90YXhhX21lYW4gPSBtZWFuKEd1aW5hcmRpYSwgbmEucm0gPSBUUlVFKSkKCiMgIyBGaWx0ZXIgb3V0IHZhbHVlcyBsZXNzIHRoYW4gMQpmaWx0ZXJlZF90YXhhX2F2ZyA8LSB0YXhhX2F2ZyAlPiUKICBmaWx0ZXIoc2VsX3RheGFfbWVhbiA+IDApCgoKIyBGaW5kIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHZhbHVlcyBvZiBzZWxfdGF4YV9tZWFuCm1pbl92YWx1ZSA8LSBtaW4oZmlsdGVyZWRfdGF4YV9hdmckc2VsX3RheGFfbWVhbiwgbmEucm0gPSBUUlVFKQptYXhfdmFsdWUgPC0gbWF4KGZpbHRlcmVkX3RheGFfYXZnJHNlbF90YXhhX21lYW4sIG5hLnJtID0gVFJVRSkKCiMgIyBBZGp1c3QgdGhlIGNvbG9yIHBhbGV0dGUgd2l0aCBzcGVjaWZpZWQgdmFsdWVzCiMgY29sb3JfcGFsZXR0ZSA8LSBjb2xvck51bWVyaWMocGFsZXR0ZSA9ICJPcmFuZ2VzIiwgZG9tYWluID0gYyhtaW5fdmFsdWUsIG1heF92YWx1ZSkpCgojICMgQ3JlYXRlIHRoZSBtYXAKIyBtYXAgPC0gbGVhZmxldChmaWx0ZXJlZF90YXhhX2F2ZykgJT4lCiMgICBhZGRQcm92aWRlclRpbGVzKCJFc3JpLldvcmxkSW1hZ2VyeSIpICU+JQojICAgYWRkQ2lyY2xlTWFya2VycygKIyAgICAgbG5nID0gfmxvbmdpdHVkZSwKIyAgICAgbGF0ID0gfmxhdGl0dWRlLAojICAgICByYWRpdXMgPSAxMCwKIyAgICAgY29sb3IgPSB+Y29sb3JfcGFsZXR0ZShzZWxfdGF4YV9tZWFuKSwKIyAgICAgZmlsbE9wYWNpdHkgPSAwLjgsCiMgICAgIHBvcHVwID0gfnBhc3RlKCJMaW5lIElEOiAiLCBsaW5lX2lkLCAiPGJyPkNvcGVwb2RzOiAiLCBzZWxfdGF4YV9tZWFuKQojICAgKSAlPiUKIyAgIGFkZExlZ2VuZCgKIyAgICAgcG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLAojICAgICBwYWwgPSBjb2xvcl9wYWxldHRlLAojICAgICB2YWx1ZXMgPSBjKG1pbl92YWx1ZSwgbWF4X3ZhbHVlKSwgICMgQWRqdXN0ZWQgdmFsdWVzIGhlcmUKIyAgICAgdGl0bGUgPSAiQ29wZXBvZCBvY2N1cnJlbmNlcyIsCiMgICAgIG9wYWNpdHkgPSAxCiMgICApCiMgbWFwCgojICMgTWFwIHdpdGggYmF0aHltZXRyeQogICMgT3ZlcmxheSBjb2xvci1zY2FsZWQgZG90cwpkdCA8LSBkYXRhLmZyYW1lKAogIGxvbiA9IGZpbHRlcmVkX3RheGFfYXZnJGxvbmdpdHVkZSwgCiAgbGF0ID0gZmlsdGVyZWRfdGF4YV9hdmckbGF0aXR1ZGUsIAogIHNlbF9wYXJhbSA9IGZpbHRlcmVkX3RheGFfYXZnJHNlbF90YXhhX21lYW4pCgojICMgQ29sb3JzOgojIEFjYW50aGFyZWEgPSBkYXJrdHVycXVvaXNlOyBicmVha3MgPSBjKDAuMjUsIDEsIDIuNSwgNSk7IGxpbWl0cyA9IGMoMC4xLCAxMCkKIyBDb3BlcG9kcyA9IHJlZDsgYnJlYWtzID0gYygwLjI1LCAxLCAyLjUsIDUsIDEwKTsgbGltaXRzID0gYygwLjEsIDE1KQojIENoYWluIGRpYXRvbXMgPSBvcmFuZ2U7IGJyZWFrcyA9IGMoMC41LCAxLCAxMCwgMzAsIDYwKTsgYygwLjEsIDcwKQojIENoYWV0b2Nlcm9zID0gcHVycGxlOyBicmVha3MgPSBjKDAuMjUsIDEsIDIuNSwgNSwgMTApOyBsaW1pdHMgPSBjKDAuMSwgMTUpCiMgRWNoaW5vZGVybXMgPSBtYWdlbnRhOyBicmVha3MgPSBjKDAuNSwgMSwgMywgNSk7IGxpbWl0cyA9IGMoMC4xLCA1KQojIExhcnZhY2VhbnMgPSBwbHVtOyBicmVha3MgPSBjKDAuNSwgMSwgMywgNSk7IGxpbWl0cyA9IGMoMC4xLCA1KQojIFBvbHljaGFldGVzID0gZG9kZ2VyYmx1ZTsgYnJlYWtzID0gYygwLjI1LCAwLjUsIDEsIDMpOyBsaW1pdHMgPSBjKDAuMSwgMykKIyBKZWxsaWVzID0gc2xhdGVncmF5NDsgYnJlYWtzID0gYygwLjI1LCAwLjUsIDEsIDIpOyBsaW1pdHMgPSBjKDAuMSwgMykKICAgIApvY2NfbWFwIDwtIGJhc2VtYXAobGltaXRzID0gYygtODQsIC03OS41LCAyNCwgMjguNSksIGJhdGh5bWV0cnkgPSBUUlVFKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZHQsIGFlcyh4ID0gbG9uLCB5ID0gbGF0LCBzaXplID0gc2VsX3BhcmFtKSwgZmlsbCA9ICJvbGl2ZWRyYWIyIiwgY29sb3IgPSAib2xpdmVkcmFiMiIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBmaWx0ZXJlZF90YXhhX21ldGEsIGFlcyh4ID0gZGVjX2xvbiwgeSA9IGRlY19sYXQpLCBjb2xvciA9ICJibGFjayIsIHNoYXBlID0gMywgc2l6ZSA9IDEuNSkgKwogIHNjYWxlX3NpemVfY29udGludW91cyhuYW1lID0gIkF2ZXJhZ2UgY291bnRzIiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMC41LCAxLCAzLCA1LCAxMCksCiAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMC4xLCA0MCksCiAgICAgICAgICAgICAgICAgICAgICAgIHJhbmdlID0gYygxLCAxNSkpICsKICB0aGVtZV9taW5pbWFsKCkgCm9jY19tYXAKCiMgb2NjX21hcF9sZWcgPC0gZ2dwbG90KGR0LCBhZXMobG9uLCBsYXQpKSArCiMgICBnZW9tX3BvaW50KGFlcyhzaXplID0gc2VsX3BhcmFtKSwgZmlsbCA9ICJncmVlbiIsIGNvbG9yID0iZ3JlZW4iKSArCiMgICB0aGVtZV9taW5pbWFsKCkKIyBvY2NfbWFwX2xlZwpgYGAKCgojIENyZWF0ZSB0aW1lLXNlcmllcyBwbG90cyBvZiBzZWxlY3RlZCB0YXhhIGF0IHNwZWNpZmljIHN0YXRpb25zCmBgYHtyfQpzZWxlY3RlZF92YXJpYWJsZSA8LSAnQ2hhZXRvY2Vyb3MnCiMgIyBMb3dlciBLZXlzCiMgc2VsZWN0ZWRfbGluZV9pZCA8LSBjKCdMSycsJ1dTJywnTU8nLCdLVycpCiMgIyBNaWRkbGUgS2V5cwojIHNlbGVjdGVkX2xpbmVfaWQgPC0gYygnQ08nLCAnQ1InKQojICMgT3RoZXIgcmVnaW9ucwpzZWxlY3RlZF9saW5lX2lkIDwtIGMoJ0NBTCcpCgojIEZpbHRlciB0aGUgZGF0YSBmb3IgdGhlIHNlbGVjdGVkIGxpbmVfaWQKZmlsdGVyZWRfbWVyZ2VkX2RhdGEgPC0gbWVyZ2VkX2RhdGFbbWVyZ2VkX2RhdGEkbGluZV9pZCAlaW4lIHNlbGVjdGVkX2xpbmVfaWQsIF0KIyBSZW1vdmUgcm93cyB3aXRoIE5BcyBpbiByZWxldmFudCBjb2x1bW5zCm1vbnRobHlfbWVhbnMgPC0gbmEub21pdChmaWx0ZXJlZF9tZXJnZWRfZGF0YVssIGMoImRhdGUiLCBzZWxlY3RlZF92YXJpYWJsZSwgImxpbmVfaWQiKV0pICU+JQogIGdyb3VwX2J5KHllYXJfbW9udGggPSBmb3JtYXQoZGF0ZSwgIiVZLSVtIikpICU+JQogIHN1bW1hcmlzZShtZWFuX3ZhbHVlID0gbWVhbighIXN5bShzZWxlY3RlZF92YXJpYWJsZSksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICBzZV92YWx1ZSA9IHNkKCEhc3ltKHNlbGVjdGVkX3ZhcmlhYmxlKSwgbmEucm0gPSBUUlVFKSAvIHNxcnQobigpKSkKCiMgQ3JlYXRlIGEgdGltZSBzZXJpZXMgcGxvdApnZ3Bsb3QobW9udGhseV9tZWFucywgYWVzKHggPSB5ZWFyX21vbnRoLCB5ID0gbWVhbl92YWx1ZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJvcmFuZ2UiKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IG1lYW5fdmFsdWUgLSBzZV92YWx1ZSwgeW1heCA9IG1lYW5fdmFsdWUgKyBzZV92YWx1ZSksCiAgICAgICAgICAgICAgICB3aWR0aCA9IDAuMiwgIyBBZGp1c3Qgd2lkdGggYXMgbmVlZGVkCiAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC44KSkgKyAjIEFkanVzdCB3aWR0aCBhcyBuZWVkZWQKICBsYWJzKHggPSAiWWVhci1Nb250aCIsIHkgPSAiTWVhbiBWYWx1ZSIpICsKICBnZ3RpdGxlKCJNb250aGx5IE1lYW5zIG9mIFNlbGVjdGVkIFZhcmlhYmxlIikgKwogIHRoZW1lX21pbmltYWwoKQoKYGBgCgoKCgoKCiMgTWF0Y2ggZmlsZSBuYW1lIHdpdGggc3RyaW5nIElEIC0gRE8gTk9UIFVTRQpgYGB7cn0KIyBsaWJyYXJ5KHRpZHl2ZXJzZSkKIyBsaWJyYXJ5KGx1YnJpZGF0ZSkKIyBsaWJyYXJ5KGRwbHlyKQojIAojICMgZXh0cmFjdCByZWxldmFudCBwYXJ0IG9mIHRoZSBzdHJpbmdzCiMgZGYgPC0gcmJpbmQoY2xhc3MuQ29wZXBvZHMsCiMgICAgICAgICAgICAgY2xhc3MuRXVjYW1waWEsCiMgICAgICAgICAgICAgY2xhc3MuTm9jdGlsdWNhLAojICAgICAgICAgICAgIGNsYXNzLlBvbHljaGFldHMsCiMgICAgICAgICAgICAgY2xhc3MuQWNhbnRoYXJlYSwKIyAgICAgICAgICAgICBjbGFzcy5DZW50cmljLAojICAgICAgICAgICAgIGNsYXNzLkNlcmF0aXVtLAojICAgICAgICAgICAgIGNsYXNzLkNoYWV0b2Nlcm9zLAojICAgICAgICAgICAgIGNsYXNzLkNoYWluMiwKIyAgICAgICAgICAgICBjbGFzcy5DaGFpbjMsCiMgICAgICAgICAgICAgY2xhc3MuQ2hhaW40LAojICAgICAgICAgICAgIGNsYXNzLk9zdHJhY29kcywKIyAgICAgICAgICAgICBjbGFzcy5KZWxsaWVzLAojICAgICAgICAgICAgIGNsYXNzLkxhcnZhY2VhbnMsCiMgICAgICAgICAgICAgY2xhc3MucGVsbGV0cykKIyBzdWJfc3RyaW5ncyA8LSBzdWJzdHIoZGYkVjEsIHN0YXJ0ID0gMTAsIHN0b3AgPSAyMikKIyB1bmlxdWVfYWxsIDwtIHVuaXF1ZShzdWJfc3RyaW5ncykKIyAKIyAjIHNlbGVjdCB1bmlxdWUgZGF0ZXMgKHRoaXMgYWxsb3dzIHRvIHNlYXJjaCBDVEQgcmVjb3JkcyBwZXIgZGF0ZSBhbmQgdGltZSkKIyAjIFRvIGZpbmQgdW5pcXVlIGRhdGVzIGFuZCB0aW1lcyB0byBleHRyYWN0IENEVCBkYXRhIHVzZTogdW5pcXVlX2FsbFtncmVwbCgiMjAyMjEyMDkiLCB1bmlxdWVfYWxsKV0KIyAKIyBpZF9saXN0IDwtIHVuaXF1ZV9hbGwKIyBpZF9saXN0MiAgPC0gYXMuUE9TSVhjdChpZF9saXN0LCBmb3JtYXQ9IiVZJW0lZF8lSCVNIiwgdHo9IlVUQyIpCiMgCiMgY29uY19vY2NfY291bnQgPC0gZGF0YS5mcmFtZShkYXRlID0gYXMuRGF0ZShjaGFyYWN0ZXIoKSksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKIyAKIyBmb3IgKCBpIGluIHNlcV9hbG9uZyhpZF9saXN0KSl7CiMgICBhY2FudGhhIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkFjYW50aGFyZWEkVjEsIGlkX2xpc3RbaV0pKQojICAgY2VudHJpYyA8LSBhcy5kYXRhLmZyYW1lKHN0cl9jb3VudChjbGFzcy5DZW50cmljJFYxLCBpZF9saXN0W2ldKSkKIyAgIGNlcmF0aXVtIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkNlcmF0aXVtJFYxLCBpZF9saXN0W2ldKSkKIyAgIGNoYWV0b2Nlcm9zIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkNoYWV0b2Nlcm9zJFYxLCBpZF9saXN0W2ldKSkKIyAgIGNoYWV0b2cgPC0gYXMuZGF0YS5mcmFtZShzdHJfY291bnQoY2xhc3MuQ2hhZXRvZ25hdGhzJFYxLCBpZF9saXN0W2ldKSkKIyAgIGNoYWluMiA8LSBhcy5kYXRhLmZyYW1lKHN0cl9jb3VudChjbGFzcy5DaGFpbjIkVjEsIGlkX2xpc3RbaV0pKQojICAgY2hhaW4zIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkNoYWluMyRWMSwgaWRfbGlzdFtpXSkpCiMgICBjaGFpbjQgPC0gYXMuZGF0YS5mcmFtZShzdHJfY291bnQoY2xhc3MuQ2hhaW40JFYxLCBpZF9saXN0W2ldKSkKIyAgIG9zdHJhIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLk9zdHJhY29kcyRWMSwgaWRfbGlzdFtpXSkpCiMgICBjb3BlcG9kcyA8LSBhcy5kYXRhLmZyYW1lKHN0cl9jb3VudChjbGFzcy5Db3BlcG9kcyRWMSwgaWRfbGlzdFtpXSkpCiMgICBkZWNhcG9kIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkRlY2Fwb2RzJFYxLCBpZF9saXN0W2ldKSkKIyAgIGVjaGlubyA8LSBhcy5kYXRhLmZyYW1lKHN0cl9jb3VudChjbGFzcy5FY2hpbm9kZXJtcyRWMSwgaWRfbGlzdFtpXSkpCiMgICBldWNhbXBpYSA8LSBhcy5kYXRhLmZyYW1lKHN0cl9jb3VudChjbGFzcy5FdWNhbXBpYSRWMSwgaWRfbGlzdFtpXSkpCiMgICBqZWxsaWVzIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkplbGxpZXMkVjEsIGlkX2xpc3RbaV0pKQojICAgbGFydmFlIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLkxhcnZhY2VhbnMkVjEsIGlkX2xpc3RbaV0pKQojICAgbm9jdGkgPC0gYXMuZGF0YS5mcmFtZShzdHJfY291bnQoY2xhc3MuTm9jdGlsdWNhJFYxLCBpZF9saXN0W2ldKSkKIyAgIHBvbHljaGFldGVzIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLlBvbHljaGFldHMkVjEsIGlkX2xpc3RbaV0pKQojICAgdHJpY2hvIDwtIGFzLmRhdGEuZnJhbWUoc3RyX2NvdW50KGNsYXNzLlRyaWNobyRWMSwgaWRfbGlzdFtpXSkpCiMgCiMgICBBY2FudGhhcmVhIDwtIGNvbFN1bXMoYWNhbnRoYSAhPSAwKQojICAgQ2VudHJpYyA8LSBjb2xTdW1zKGNlbnRyaWMgIT0gMCkKIyAgIENlcmF0aXVtX3NwcCA8LSBjb2xTdW1zKGNlcmF0aXVtICE9IDApCiMgICBDaGFldG9jZXJvcyA8LSBjb2xTdW1zKGNoYWV0b2Nlcm9zICE9IDApCiMgICBDaGFldG9nbmF0aHMgPC0gY29sU3VtcyhjaGFldG9nICE9IDApCiMgICBEaWF0b21fY2hhaW5zXzEgPC0gY29sU3VtcyhjaGFpbjIgIT0gMCkKIyAgIERpYXRvbV9jaGFpbnNfMiA8LSBjb2xTdW1zKGNoYWluMyAhPSAwKQojICAgRGlhdG9tX2NoYWluc18zIDwtIGNvbFN1bXMoY2hhaW40ICE9IDApCiMgICBPc3RyYWNvZHMgPC0gY29sU3Vtcyhvc3RyYSAhPSAwKQojICAgQ29wZXBvZHMgPC0gY29sU3Vtcyhjb3BlcG9kcyAhPSAwKQojICAgRGVjYXBvZHMgPC0gY29sU3VtcyhkZWNhcG9kICE9IDApCiMgICBFY2hpbm9kZXJtcyA8LSBjb2xTdW1zKGVjaGlubyAhPSAwKQojICAgRXVjYW1waWFfc3BwIDwtIGNvbFN1bXMoZXVjYW1waWEgIT0gMCkKIyAgIEplbGxpZXM8LSBjb2xTdW1zKGplbGxpZXMgIT0gMCkKIyAgIExhcnZhY2VhbnMgPC0gY29sU3VtcyhsYXJ2YWUgIT0gMCkKIyAgIE5vY3RpbHVjYSA8LSBjb2xTdW1zKG5vY3RpICE9IDApCiMgICBQb2x5Y2hhZXRlcyA8LSBjb2xTdW1zKHBvbHljaGFldGVzICE9IDApCiMgICBUcmljaG9kZXNtaXVtX3NwcCA8LSBjb2xTdW1zKHRyaWNobyAhPSAwKQojIAojICAgIyBQYXJzZSB0aGUgZGF0ZS10aW1lIHN0cmluZyB3aXRoIHltZF9obSgpCiMgICBvY2NfZGF0ZXRpbWUgIDwtIGFzLlBPU0lYY3QoaWRfbGlzdFtpXSwgZm9ybWF0PSIlWSVtJWRfJUglTSIsIHR6PSJVVEMiKQojICAgb2NjX2RhdGV0aW1lX3N0ciA8LSBzdWJzdHIoaWRfbGlzdFtpXSwgMSwgMTMpCiMgCiMgICByb3dfZGYgPC0gZGF0YS5mcmFtZShkYXRlID0gb2NjX2RhdGV0aW1lLCBvY2NfZGF0ZXRpbWVfc3RyLAojICAgICAgICAgICAgICAgICAgICAgICAgQWNhbnRoYXJlYSwKIyAgICAgICAgICAgICAgICAgICAgICAgIENlbnRyaWMsCiMgICAgICAgICAgICAgICAgICAgICAgICBDZXJhdGl1bV9zcHAsCiMgICAgICAgICAgICAgICAgICAgICAgICBDaGFldG9jZXJvcywKIyAgICAgICAgICAgICAgICAgICAgICAgIENoYWV0b2duYXRocywKIyAgICAgICAgICAgICAgICAgICAgICAgIERpYXRvbV9jaGFpbnNfMSwKIyAgICAgICAgICAgICAgICAgICAgICAgIERpYXRvbV9jaGFpbnNfMiwKIyAgICAgICAgICAgICAgICAgICAgICAgIERpYXRvbV9jaGFpbnNfMywKIyAgICAgICAgICAgICAgICAgICAgICAgIE9zdHJhY29kcywKIyAgICAgICAgICAgICAgICAgICAgICAgIENvcGVwb2RzLAojICAgICAgICAgICAgICAgICAgICAgICAgRGVjYXBvZHMsCiMgICAgICAgICAgICAgICAgICAgICAgICBFY2hpbm9kZXJtcywKIyAgICAgICAgICAgICAgICAgICAgICAgIEV1Y2FtcGlhX3NwcCwKIyAgICAgICAgICAgICAgICAgICAgICAgIEplbGxpZXMsCiMgICAgICAgICAgICAgICAgICAgICAgICBMYXJ2YWNlYW5zLAojICAgICAgICAgICAgICAgICAgICAgICAgTm9jdGlsdWNhLAojICAgICAgICAgICAgICAgICAgICAgICAgUG9seWNoYWV0ZXMsCiMgICAgICAgICAgICAgICAgICAgICAgICBUcmljaG9kZXNtaXVtX3NwcAojICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgcm93bmFtZXMocm93X2RmKSA8LSBpCiMgCiMgICBjb25jX29jY19jb3VudCA8LSByYmluZChjb25jX29jY19jb3VudCwgcm93X2RmKQojIH0KIyAKIyBjb25jX29jY19maW5hbCA8LSBhcnJhbmdlKGNvbmNfb2NjX2NvdW50LCBkYXRlKQoKYGBgCgoKIyBNYXRjaCBpbWFnZSByZWNvcmRzIHdpdGggQ1REIG1ldGFkYXRhIGFuZCBzZWFzY2FwZXMgKHVzZSB3aXRoIHN0cmluZ3MgbWF0Y2hpbmcpIC0gRE8gTk9UIFVTRQpgYGB7cn0KIyAjIERpcmVjdG9yeSB3aGVyZSB0aGUgQ1REIG1ldGFkYXRhIGlzIGxvY2F0ZWQKIyBkaXJfcGF0aDIgPC0gIn4vZW5yaXF1ZW1vbnRlczAxQGdtYWlsLmNvbSAtIEdvb2dsZSBEcml2ZS9NeSBEcml2ZS9HRHJpdmUvT0NFRF9BT01ML1dTX2NydWlzZXMvcGxhbmt0b25faW1hZ2luZy9DUElDUy93c19jcnVpc2VfY3RkLyIKIyBmaWxlX25hbWUgPC0gbGlzdC5maWxlcyhwYXRoID0gZGlyX3BhdGgyLCBwYXR0ZXJuID0gIi5jc3YiLCBmdWxsLm5hbWVzID0gVFJVRSkKIyBjdGRfbWV0YSA8LSByZWFkLmNzdihmaWxlX25hbWUsIGZpbGwgPSBUUlVFKQojIAojIGR0X2xpc3QgPC0gY3RkX21ldGEkR01ULmRhdGV0aW1lCiMgCiMgY29uY19ldmVudCA8LSBkYXRhLmZyYW1lKCkKIyAKIyBmb3IgKCB0IGluIHNlcV9hbG9uZyhkdF9saXN0KSl7CiMgICBldmVudCA8LSBzdHJfY291bnQoY29uY19vY2NfZmluYWwkb2NjX2RhdGV0aW1lX3N0ciwgZHRfbGlzdFt0XSkKIyAgIGlkeF9ldmVudCA8LSB3aGljaChldmVudCA9PSAxLCBhcnIuaW5kID0gVFJVRSkKIyAgIG9jY19yb3cgPC0gY29uY19vY2NfZmluYWxbaWR4X2V2ZW50LCBdCiMgICBldmVudF9tZXRhIDwtIGN0ZF9tZXRhW2lkeF9ldmVudCwgXQojICAgY29uY19ldmVudCA8LSByYmluZChjb25jX2V2ZW50LCBvY2Nfcm93KQojIH0KIyAKIyB0YXhhX21ldGEgPC0gY2JpbmQoY3RkX21ldGEsIGNvbmNfZXZlbnQpCmBgYAo=